矩阵优化 。一篇讲解的DP博客
最开始想到
如下:令
表示当前为第
位,余数为
,最后一位为
的方案。假设相邻两位之和不为
。
最后的答案即为
复杂度
。然而这里的
,不行啊。
观察到
这两维都很小。可以压缩成
然后把上面的转化一下可以得到:
可以利用矩阵乘法来优化。因为有这三个特点
1.转移要选取的决策较少。(一般在常数级别)
2.转移的步骤很多。(一般是1e10以上的级别)
3.每一步的转移方程一样。(和递推类似)
问题可以先转成长度
的个数。用
就行了。
那么这里就要求一个前缀和。一个巧妙的解决办法:多开一格记录前缀和。转移矩阵里面多开一列(紫色部分),记录余数为0的累积和。
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int maxn=71;
inline int id(int i,int j){return i*10+j;}
inline int mul(int a,int b){return (long long)a*b%mod;}
inline int add(int a,int b){return (a+b)>=mod ? (a+b)%mod : a+b;}
struct matrix{
int a[maxn][maxn];
matrix(int t=0){
memset(a,0,sizeof(a));
for(int i=0;i<maxn;++i) a[i][i]=t;
}
friend inline matrix operator*(const matrix &a,const matrix &b){
matrix ret;
for(int i=0;i<maxn;++i)
for(int j=0;j<maxn;++j)
for(int k=0;k<maxn;++k)
ret.a[i][j]=add(ret.a[i][j],mul(a.a[i][k],b.a[k][j]));
return ret;
}
friend inline matrix operator^(matrix a,int b){
matrix ret(1);
for(;b;a=a*a,b>>=1) if(b&1) ret=ret*a;
return ret;
}
};
int n,l,r,k;
int main(){
cin>>n;
while(n--){
cin>>l>>r>>k;matrix A,B;
for(int i=0;i<7;++i)
for(int j=0;j<10;++j)
for(int x=0;x<10;++x) if(j+x!=k)
B.a[id(i,j)][id((i*10+x)%7,x)]++;
for(int i=0;i<10;++i) B.a[id(0,i)][maxn-1]++;
B.a[maxn-1][maxn-1]=1;
for(int i=1;i<10;++i) A.a[0][id(i%7,i)]++;
matrix a=A*(B^r),b=A*(B^(l-1));
int ans=((a.a[0][maxn-1]-b.a[0][maxn-1])+mod)%mod;
printf("%d\n",ans);
}
}