建信金融科技力扣专场竞赛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 矩阵快速幂
AtCoder abc256全题解(区间合并模板矩阵快速幂优化dp线段树……)