题意:一段长为 i 的项链有 a[i] 种装饰方式,问长度为n的相连共有多少种装饰方式
分析:采用dp做法,dp[i]=∑dp[j]*a[i-j]+a[i],(1<=j<=i-1)
然后对于这种递推式,也就是dp[i]等于前j个dp数组和a数组的卷积,然后可看所有的
一看n是1e5,所以暴力超时,然后采用cdq分治加速,这种卷积递推通常采用cdq分治加速
cdq的话很简单了,就是先递归左边,算左对右的贡献,递归右边就行,一半一半更新
#include <cstdio> #include <vector> #include <stack> #include <cmath> #include <cstring> #include <algorithm> using namespace std; const int N = 1e5+5; const int mod = 313; typedef long long LL; const double pi = acos(-1.0); int a[N],dp[N],n; struct complex{ double r,i; complex(double R=0,double I=0){ r=R;i=I; } complex operator+(const complex &a)const{ return complex(r+a.r,i+a.i); } complex operator-(const complex &a)const{ return complex(r-a.r,i-a.i); } complex operator*(const complex &a)const{ return complex(r*a.r-i*a.i,r*a.i+i*a.r); } }x[N*4],y[N*4]; void change(complex x[],int len){ int i,j,k; for(i=1,j=len/2;i<len-1;++i){ if(i<j)swap(x[i],x[j]); k=len/2; while(j>=k){j-=k;k>>=1;} if(j<k)j+=k; } } void fft(complex x[],int len,int on){ change(x,len); for(int i=2;i<=len;i<<=1){ complex wn(cos(-on*2*pi/i),sin(-on*2*pi/i)); for(int j=0;j<len;j+=i){ complex w(1,0); for(int k=j;k<j+i/2;++k){ complex u = x[k]; complex t = w*x[k+i/2]; x[k]=u+t; x[k+i/2]=u-t; w=w*wn; } } } if(on==-1)for(int i=0;i<len;++i)x[i].r/=len; } void up(int &x){ x%=mod; } void cdq(int l,int r){ if(l==r){dp[l]+=a[l];up(dp[l]);return;} int mid=l+r>>1; cdq(l,mid); int len=1; while(len<=(r-l+1))len<<=1; for(int i=0;i<len;++i)x[i]=y[i]=complex(0,0); for(int i=l;i<=mid;++i)x[i-l]=complex(dp[i],0); for(int i=1;i<=r-l+1;++i)y[i-1]=complex(a[i],0); fft(x,len,1);fft(y,len,1); for(int i=0;i<len;++i)x[i]=x[i]*y[i]; fft(x,len,-1); for(int i=mid+1;i<=r;++i) dp[i]+=(int)(x[i-l-1].r+0.5),up(dp[i]); cdq(mid+1,r); } int main(){ while(~scanf("%d",&n),n){ for(int i=1;i<=n;++i){ scanf("%d",&a[i]);up(a[i]);dp[i]=0; } cdq(1,n); printf("%d ",dp[n]); } return 0; }