CSA49G
XSY3315
因为判断两串是否本质不同只看某几项是不是好数,与究竟是哪个好数无关,所以考虑转换一下题意:
给出一个长度为 a k a_k ak的01串 S S S,第 a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak项为1,其余项为0。
现要用若干个 S S S的前缀拼出串 T T T,使得 T T T中有 n n n个1,以1结尾,并且任意两个1之间0的个数不超过 m m m。问所有不同的 T T T的长度之和为多少?
S S S如果用01串表示,会很长,发现 k k k很小,考虑换一种表示方式:设 b i = a i − a i − 1 b_i=a_i-a_{i-1} bi=ai−ai−1。我们把 S S S串描述成 { b 1 , b 2 , b 3 , . . . , b k } \{b_1,b_2,b_3,...,b_k\} { b1,b2,b3,...,bk},表示 S S S由 ( b 1 − 1 ) (b_1-1) (b1−1)个0,1个1, ( b 2 − 1 ) (b_2-1) (b2−1)个0,1个1,…, ( b k − 1 ) (b_k-1) (bk−1)个0,1个1 拼接而成。
设 g ( x , i ) g(x,i) g(x,i)表示目前有 x x x个1,以 ( b i − 1 ) (b_i-1) (bi−1)个0+1个1 结尾的01串个数,
f ( x , i ) f(x,i) f(x,i)表示目前有 x x x个1,以 ( b i − 1 ) (b_i-1) (bi−1)个0+1个1 结尾的01串长度之和。
可以列出dp式:
g ( x , i ) = { g ( x − 1 , i − 1 ) i > 1 ∑ j = 1 k g ( x − 1 , j ) × ( m − a [ 1 ] + 1 ) i = 1 g(x,i)=\begin{cases}g(x-1,i-1)\qquad i>1\\\sum_{j=1}^{k}g(x-1,j)\times(m-a[1]+1)\qquad i=1\end{cases} g(x,i)={ g(x−1,i−1)i>1∑j=1kg(x−1,j)×(m−a[1]+1)i=1
f ( x , i ) = { f ( x − 1 , i − 1 ) + ( a [ i ] − a [ i − 1 ] ) × g ( x − 1 , i − 1 ) i > 1 ∑ j = 1 k f ( x − 1 , j ) × ( m − a [ 1 ] + 1 ) + g ( x − 1 , j ) × ( a [ 1 ] + m ) ( m − a [ 1 ] + 1 ) 2 i = 1 f(x,i)=\begin{cases}f(x-1,i-1)+(a[i]-a[i-1])\times g(x-1,i-1)\qquad i>1\\\sum_{j=1}^{k}f(x-1,j)\times(m-a[1]+1)+g(x-1,j)\times\frac{(a[1]+m)(m-a[1]+1)}{2}\qquad i=1\end{cases} f(x,i)={ f(x−1,i−1)+(a[i]−a[i−1])×g(x−1,i−1)i>1∑j=1kf(x−1,j)×(m−a[1]+1)+g(x−1,j)×2(a[1]+m)(m−a[1]+1)i=1
把式子用矩阵快速幂优化一下,复杂度是 O ( k 3 l o g n ) O(k^3logn) O(k3logn)
(ps.因为 f f f的转移同时与 f , g f,g f,g有关,所以通过矩乘转移的过程有点特殊,具体可以看代码。)
然而还有一个小小的问题:
假设 a = { 2 , 6 , 7 , 9 , 13 } a=\{2,6,7,9,13\} a={
2,6,7,9,13},
那么 S [ 1 − 13 ] = 0100011010001 S[1-13]=0100011010001 S[1−13]=0100011010001, S [ 1 − 6 ] = 010001 S[1-6]=010001 S[1−6]=010001, S [ 1 − 7 ] = 0100011 S[1-7]=0100011 S[1−7]=0100011。
我们发现 S [ 1 − 7 ] + S [ 1 − 6 ] = S [ 1 − 13 ] S[1-7]+S[1-6]=S[1-13] S[1−7]+S[1−6]=S[1−13],即若 S [ 1 − y ] S[1-y] S[1−y]是 S [ 1 − x ] S[1-x] S[1−x]的后缀,则 S [ 1 − x ] S[1-x] S[1−x]可以由 S [ 1 − y ] S[1-y] S[1−y], S [ 1 − ( x − y ) ] S[1-(x-y)] S[1−(x−y)]拼成。但仔细看上面的dp式就会发现 S [ 1 − 7 ] + S [ 1 − 6 ] S[1-7]+S[1-6] S[1−7]+S[1−6]和 S [ 1 − 13 ] S[1-13] S[1−13]被算成了两个串。怎么办?暴力预处理一下,对于前缀 S [ 1 − x ] S[1-x] S[1−x],若有前缀 S [ 1 − y ] S[1-y] S[1−y]同时是 S [ 1 − x ] S[1-x] S[1−x]的后缀,则前缀 S [ 1 − x ] S[1-x] S[1−x]不能取。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int K=110;
int kk,m,n,a[K],pd[K][K],flag[K],ans;
struct Matrix{
int n,m,g[K][K],f[K][K];
//g:个数 f:长度和
Matrix(int x=0,int y=0){
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
n=x,m=y;
}
friend Matrix operator * (Matrix a,Matrix b){
Matrix res(a.n,b.m);
for(int i=1;i<=a.n;i++){
for(int j=1;j<=b.m;j++){
__int128 g=0,f=0;
for(int k=1;k<=a.m;k++){
g+=1ll*a.g[i][k]*b.g[k][j];
f+=1ll*a.f[i][k]*b.g[k][j]+1ll*a.g[i][k]*b.f[k][j];
}
res.g[i][j]=int(g%mod);
res.f[i][j]=int(f%mod);
}
}
return res;
}
};
int Sum(ll x,ll y){
return (x+y)*(y-x+1)/2%mod;
}
int main(){
scanf("%d%d%d",&kk,&m,&n);
for(int i=1;i<=kk;i++) scanf("%d",&a[i]);
sort(a+1,a+kk+1);
pd[1][1]=1;
for(int i=2;i<=kk;i++){
int fl=0;
for(int j=2;j<=i;j++){
if(pd[i-1][j-1]==1) fl=1;
if(pd[i-1][j-1]==1&&(a[j]-a[j-1])==(a[i]-a[i-1])) pd[i][j]=1;
}
if(a[1]<=a[i]-a[i-1]&&fl) pd[i][1]=1;
for(int j=1;j<i;j++)
if(pd[i][j]==1) flag[i]=1;
}
Matrix A(kk,kk),res(1,kk);
for(int i=1;i<=kk;i++){
if(flag[i]==0){
A.g[i][1]=max(m-a[1]+1,0);
A.f[i][1]=max(Sum(a[1],m),0);
}
}
for(int i=1;i<kk;i++){
if(a[i+1]-a[i]<=m){
A.g[i][i+1]=1;
A.f[i][i+1]=a[i+1]-a[i];
}
}
res.g[1][1]=max(m-a[1]+1,0);
res.f[1][1]=max(Sum(a[1],m),0);
int b=n-1;
while(b){
if(b&1) res=res*A;
b>>=1;A=A*A;
}
for(int i=1;i<=kk;i++){
if(flag[i]==0) ans=(ans+res.f[1][i])%mod;
}
printf("%d\n",ans);
return 0;
}