矩阵优化 D P DP DP。一篇讲解的DP博客
最开始想到
D
P
DP
DP如下:令
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示当前为第
i
i
i位,余数为
j
j
j,最后一位为
k
k
k的方案。假设相邻两位之和不为
b
a
n
ban
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]
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]}
l<=i<=r,0<=k<=9∑dp[i][0][k]
复杂度
O
(
n
∗
7
∗
10
∗
10
)
O(n*7*10*10)
O(n∗7∗10∗10)。然而这里的
n
<
=
1
e
9
n<=1e9
n<=1e9,不行啊。
观察到
[
j
]
[
k
]
[j][k]
[j][k]这两维都很小。可以压缩成
[
j
∗
10
+
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]
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,n]的个数。用
(
[
1
,
r
]
−
[
1
,
l
−
1
]
)
([1,r]-[1,l-1])
([1,r]−[1,l−1])就行了。
那么这里就要求一个前缀和。一个巧妙的解决办法:多开一格记录前缀和。转移矩阵里面多开一列(紫色部分),记录余数为0的累积和。
乘的时候是左边的一行乘右边对应的一列。记在一行的对应格。
横行对应为
x
x
x,纵列对应为
y
y
y,那么
B
[
x
]
[
y
]
B[x][y]
B[x][y]表示从
x
x
x转移到
y
y
y的方案数。
最右一列打勾的是记录当前长度满足条件的方案数要用,下面那个阴影的格子是记前缀和要用。因为是乘积相加,求和之后就把总和缩到实心的紫色格子了。也就是长度为
[
1
,
n
]
[1,n]
[1,n]的总方案数。
#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);
}
}