hdu5564 Clarke and digits

传送门

矩阵优化 D P DP 一篇讲解的DP博客

最开始想到 D P DP 如下:令 d p [ i ] [ j ] [ k ] dp[i][j][k] 表示当前为第 i i 位,余数为 j j ,最后一位为 k k 的方案。假设相邻两位之和不为 b a n ban
i f ( x + k ! = b a n ) = > d p [ i + 1 ] [ ( j 10 + x ) % 7 ] [ x ] + = d p [ i ] [ j ] [ k ] if(x+k!=ban)=>dp[i+1][(j*10+x)\%7][x]+=dp[i][j][k]
最后的答案即为 l < = i < = r , 0 < = k < = 9 d p [ i ] [ 0 ] [ k ] \sum_{l<=i<=r,0<=k<=9}{dp[i][0][k]}
复杂度 O ( n 7 10 10 ) O(n*7*10*10) 。然而这里的 n < = 1 e 9 n<=1e9 ,不行啊。

观察到 [ j ] [ k ] [j][k] 这两维都很小。可以压缩成 [ j 10 + k ] [j*10+k] 然后把上面的转化一下可以得到:
i f ( x + k ! = b a n ) = > d p [ i + 1 ] [ (   ( j 10 + x ) % 7   ) 10 + x ] + = d p [ i ] [ j 10 + k ] if(x+k!=ban)=>dp[i+1][(\ (j*10+x)\%7\ )*10+x]+=dp[i][j*10+k]
可以利用矩阵乘法来优化。因为有这三个特点

1.转移要选取的决策较少。(一般在常数级别)
2.转移的步骤很多。(一般是1e10以上的级别)
3.每一步的转移方程一样。(和递推类似)

问题可以先转成长度 [ 1 , n ] [1,n] 的个数。用 ( [ 1 , r ] [ 1 , l 1 ] ) ([1,r]-[1,l-1]) 就行了。
那么这里就要求一个前缀和。一个巧妙的解决办法:多开一格记录前缀和。转移矩阵里面多开一列(紫色部分),记录余数为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);
	}
}

猜你喜欢

转载自blog.csdn.net/g21wcr/article/details/88583929