题目链接:
BNUOJ 27935 - 我爱背单词 (中文题面)
分析:
原题数据范围很小,可以直接暴力计算新单词对每一天需要背的单词量的贡献,
注意到这个过程实质是一个多项式乘法,第Q天所需要背的单词量为多项式
(N[1]*x+N[2]*x^2+...+N[D]*x^D)(1+x^(R[1]-1)+x^(R[2]-1)+...+x^(R[k]-1))
展开式中x^Q项的系数,可以使用FFT算法进行求解。
优化后的算法可以处理更大范围的数据,视情形考虑使用不依赖浮点数精度的NTT算法。
代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> using namespace std; typedef double db; db PI=acos(-1.0); struct Complex { db x,y; Complex(db x=0.0,db y=0.0):x(x),y(y){} Complex operator + (const Complex &b)const { return Complex(x+b.x,y+b.y); } Complex operator - (const Complex &b)const { return Complex(x-b.x,y-b.y); } Complex operator * (const Complex &b)const { return Complex(x*b.x-y*b.y,x*b.y+y*b.x); } }; void change(Complex y[],int len) { for(int i=1,j=len/2;i<len-1;i++) { if(i<j)swap(y[i],y[j]); int k=len/2; while(j>=k) { j-=k; k/=2; } if(j<k)j+=k; } } void fft(Complex y[],int len,int on) { change(y,len); for(int h=2;h<=len;h<<=1) { Complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h)); for(int j=0;j<len;j+=h) { Complex w(1,0); for(int k=j;k<j+h/2;k++) { Complex u=y[k]; Complex t=w*y[k+h/2]; y[k]=u+t; y[k+h/2]=u-t; w=w*wn; } } } if(on==-1) for(int i=0;i<len;i++) y[i].x/=len; } const int MAXN=4096; Complex x1[MAXN],x2[MAXN]; int N[105],R[105]; int main() { int T; scanf("%d",&T); while(T--) { int D; scanf("%d",&D); for(int i=1;i<=D;i++)scanf("%d",&N[i]); int K; scanf("%d",&K); for(int i=1;i<=K;i++)scanf("%d",&R[i]); memset(x1,0,sizeof(x1)); memset(x2,0,sizeof(x2)); for(int i=1;i<=D;i++)x1[i].x=N[i]; for(int i=1;i<=K;i++)x2[R[i]-1].x=1; x2[0].x+=1; fft(x1,MAXN,1); fft(x2,MAXN,1); for(int i=0;i<MAXN;i++)x1[i]=x1[i]*x2[i]; fft(x1,MAXN,-1); int M; scanf("%d",&M); while(M--) { int Q; scanf("%d",&Q); if(Q>=MAXN)printf("0\n"); else printf("%d\n",(int)(x1[Q].x+0.5)); } } return 0; }