[CSA49G][XSY3315] Bunny on Number Line (DP)

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=aiai1。我们把 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) (b11)个0,1个1, ( b 2 − 1 ) (b_2-1) (b21)个0,1个1,…, ( b k − 1 ) (b_k-1) (bk1)个0,1个1 拼接而成。

g ( x , i ) g(x,i) g(x,i)表示目前有 x x x个1,以 ( b i − 1 ) (b_i-1) (bi1)个0+1个1 结尾的01串个数,
f ( x , i ) f(x,i) f(x,i)表示目前有 x x x个1,以 ( b i − 1 ) (b_i-1) (bi1)个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(x1,i1)i>1j=1kg(x1,j)×(ma[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(x1,i1)+(a[i]a[i1])×g(x1,i1)i>1j=1kf(x1,j)×(ma[1]+1)+g(x1,j)×2(a[1]+m)(ma[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[113]=0100011010001 S [ 1 − 6 ] = 010001 S[1-6]=010001 S[16]=010001 S [ 1 − 7 ] = 0100011 S[1-7]=0100011 S[17]=0100011
我们发现 S [ 1 − 7 ] + S [ 1 − 6 ] = S [ 1 − 13 ] S[1-7]+S[1-6]=S[1-13] S[17]+S[16]=S[113],即若 S [ 1 − y ] S[1-y] S[1y] S [ 1 − x ] S[1-x] S[1x]的后缀,则 S [ 1 − x ] S[1-x] S[1x]可以由 S [ 1 − y ] S[1-y] S[1y], S [ 1 − ( x − y ) ] S[1-(x-y)] S[1(xy)]拼成。但仔细看上面的dp式就会发现 S [ 1 − 7 ] + S [ 1 − 6 ] S[1-7]+S[1-6] S[17]+S[16] S [ 1 − 13 ] S[1-13] S[113]被算成了两个串。怎么办?暴力预处理一下,对于前缀 S [ 1 − x ] S[1-x] S[1x],若有前缀 S [ 1 − y ] S[1-y] S[1y]同时是 S [ 1 − x ] S[1-x] S[1x]的后缀,则前缀 S [ 1 − x ] S[1-x] S[1x]不能取。

#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;
}

猜你喜欢

转载自blog.csdn.net/Emma2oo6/article/details/120561181