建信金融科技力扣专场竞赛D——矩阵快速幂优化dp+卡常

Posted hans774882968

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了建信金融科技力扣专场竞赛D——矩阵快速幂优化dp+卡常相关的知识,希望对你有一定的参考价值。

传送门

又是被力扣虐爆的一天,绝绝子

显然按列的升序排序,然后分段地对a[i]a[i+1]进行答案计算,乘到ans上即可。但dp要转移1e9次。于是比赛时我sb了,觉得这题dp做不了,往组合数学方面建模了,最后没有写出来。

因为dp只涉及dp[i-1]dp[i],并且注意到数据范围row <= 20,所以是可以矩阵快速幂优化的。初始向量:

Mat t(row,1);
t.m[a[i][1]][0] = 1;

注:这里我们为了方便排序,把行和列交换了一下,a[i][1]表示点i的行编号

转移矩阵:

Mat M(row);
re_(i,0,row){
    if(i) M.m[i][i-1] = 1;
    M.m[i][i] = 1;
    if(i+1 < row) M.m[i][i+1] = 1;
}

路径数的贡献:

int b = a[i+1][0]-a[i][0];//dp需要计算多少列
Mat res = mul(q_pow(b,mod,row),t,mod);
//贡献为res.m[a[i+1][1]][0]

知道正解还不够,本题严重卡常,因此需要一些卡常技巧。最关键的卡常技巧:

  • 需要预处理转移矩阵的2的幂的结果,减少矩阵乘法的调用次数,从而加速快速幂函数。
  • 需要修改矩阵乘法的计算方式,让它每加8次再取模1次。之所以加8次,是因为LL上限大约9e18,而矩阵乘法中,每次计算结果的增量最大为1e9*1e9。取模是十分耗时的运算,需要尽量减少它

修改后的快速幂函数:

Mat q_pow(int b,int mod,int row){
    Mat ret(row);re_(i,0,row) ret.m[i][i] = 1;
    for(int i = 0;b;b >>= 1,++i){
        if(b & 1) ret = mul(ret,tmp[i],mod);
    }
    return ret;
}

修改后的矩阵乘法:

Mat mul(Mat a,Mat b,int mod){
    Mat ret(a.r,b.c);
    re_(i,0,a.r){
        re_(k,0,a.c){
            re_(j,0,b.c){
                ret.m[i][j] += a.m[i][k]*b.m[k][j];
                if((k & 7) == 7) ret.m[i][j] %= mod;
            }
        }
    }
    re_(i,0,a.r) re_(j,0,b.c) ret.m[i][j] %= mod;
    return ret;
}

代码和一些测试数据

#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
typedef long long LL;
struct Mat{
    LL m[20][20];int r,c;
    Mat(){}
    Mat(int sz,bool fl = true):r(sz),c(sz){
        if(!fl) return;
        if(r*c < 100) re_(i,0,r) re_(j,0,c) m[i][j] = 0;
        else memset(m,0,sizeof m);
    }
    Mat(int r,int c,bool fl = true):r(r),c(c){
        if(!fl) return;
        if(r*c < 100) re_(i,0,r) re_(j,0,c) m[i][j] = 0;
        else memset(m,0,sizeof m);
    }
};
vector<Mat> tmp;
Mat mul(Mat a,Mat b,int mod){
    Mat ret(a.r,b.c);
    re_(i,0,a.r){
        re_(k,0,a.c){
            re_(j,0,b.c){
                ret.m[i][j] += a.m[i][k]*b.m[k][j];
                if((k & 7) == 7) ret.m[i][j] %= mod;
            }
        }
    }
    re_(i,0,a.r) re_(j,0,b.c) ret.m[i][j] %= mod;
    return ret;
}
Mat q_pow(int b,int mod,int row){
    Mat ret(row);re_(i,0,row) ret.m[i][i] = 1;
    for(int i = 0;b;b >>= 1,++i){
        if(b & 1) ret = mul(ret,tmp[i],mod);
    }
    return ret;
}
class Solution {
public:
    static const int mod = 1e9 + 7;
    void init_mat(int row,int col){
        Mat M(row);
        re_(i,0,row){
            if(i) M.m[i][i-1] = 1;
            M.m[i][i] = 1;
            if(i+1 < row) M.m[i][i+1] = 1;
        }
        tmp.clear();
        tmp.push_back(M);
        int num = 0;for(;col;col >>= 1,++num);
        rep(i,1,num) tmp.push_back(mul(tmp.back(),tmp.back(),mod));
    }
    int electricityExperiment(int row, int col, vector<vector<int>>& a) {
        const int n = a.size();
        re_(i,0,n) swap(a[i][0],a[i][1]);
        sort(a.begin(),a.end());
        init_mat(row,col);
        LL ans = 1;
        re_(i,0,n-1){
            Mat t(row,1);t.m[a[i][1]][0] = 1;
            int b = a[i+1][0]-a[i][0];
            if(!b) return 0;
            Mat res = mul(q_pow(b,mod,row),t,mod);
            ans = ans * res.m[a[i+1][1]][0] % mod;
        }
        return ans;
    }
};
/* 0 3 6 34170816 386122361
5
6
[[1, 3], [3, 2], [4, 1]]
3
4
[[0, 3], [2, 0]]
5
6
[[1, 3], [3, 5], [2, 0]]
4
30
[[1, 28], [2, 9], [0, 27], [3, 5], [1, 21]]
6
128
[[0, 34], [3, 45], [2, 49], [5, 94], [2, 100], [1, 121], [2, 90]]
*/

以上是关于建信金融科技力扣专场竞赛D——矩阵快速幂优化dp+卡常的主要内容,如果未能解决你的问题,请参考以下文章

2017中国大学生程序设计竞赛 - 网络选拔赛 HDU 6155 Subsequence Count 矩阵快速幂

排队 矩阵快速幂优化dp

AtCoder abc256全题解(区间合并模板矩阵快速幂优化dp线段树……)

AtCoder abc256全题解(区间合并模板矩阵快速幂优化dp线段树……)

CodeForces621E 快速矩阵幂优化dp

矩阵快速幂 优化dp 模板