先贴题目连接:http://acm.zju.edu.cn/changsha/showProblem.do?problemId=28
题意是给一个数n(1<=n<=80000),问最多用三个素数,通过+,*表示n共有多少种方法。例如:8=2*2*2=2+2*3=2+3+3=3+5,所以共4种。另外注意一下,相同的数不同的符号也记作不同的表示,例如4=2*2=2+2,这是两种不同的表示...
首先筛素数,之后a,a*b,a*b*c,a*b+c都可以暴力求出来,那么难处理的主要就是a+b和a+b+c这两种情况,这里用两次fft来计算,第一次求出k=a+b中每个k各有多少中表示方法,这里注意判一下重,删掉a+a的情况以及其他情况/2,第二次在求出k的基础上,求出l=k+c共有多少种表示方法,这里注意补上a+b+a的情况后,在处理掉a+b+c==c+a+b的情况,最后特判一下a+a+a的情况就是最后的答案了。另外8000内的素数一共也就不到8000个,所以这题直接暴力预处理也能做..这里拿来做fft的练习了...
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <cstring> using namespace std; typedef long long ll; const int maxn=80080; const int mod=1000000007; bool ok[maxn]; ll prime[maxn]; int cnt=0; const double PI = acos(-1.0); struct comp { double r,i; comp(double rt=0,double it=0) { r=rt; i=it; } comp operator +(const comp& b) { return comp(r+b.r,i+b.i); } comp operator -(const comp &b) { return comp(r-b.r,i-b.i); } comp operator *(const comp &b) { return comp(r*b.r-i*b.i,r*b.i+i*b.r); } }; void change(comp y[],int len)//二进制转置--雷德算法 { int i,j,k; for(i = 1, j = len/2;i < len-1;i++) { if(i < j)swap(y[i],y[j]); k = len/2; while( j >= k) { j -= k; k /= 2; } if(j < k)j += k; } } void fft(comp y[],int len,int on) /* on=1 DFT 把一个多项式的系数向量转化为点集表示; on=-1,IDFT 把一个点集转化成多项式的系数向量*/ { change(y,len); for(int h = 2;h <= len;h <<= 1) { comp wn(cos(-on*2*PI/h),sin(-on*2*PI/h)); for(int j = 0;j < len;j += h) { comp w(1,0); for(int k = j;k < j+h/2;k++) { comp u = y[k]; comp 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].r /= len; } void conv(comp f[],int len)//求f的卷积 { fft(f,len,1); for (int i=0; i<len; i++) f[i]=f[i]*f[i]; fft(f,len,-1); } void ss() { memset(ok,true,sizeof ok); ok[2]=true; ok[1]=false; ok[0]=false; for (int i=2; i<maxn; i++) { if (ok[i]) { for (int j=i+i; j<maxn; j+=i) if (ok[j]) ok[j]=false; } } cnt=0; for (int i=2; i<maxn; i++) if (ok[i]) { prime[cnt++]=i; } // for (int i=0; i<cnt; i++) // cout<<prime[i]<<"\n"; // cout<<endl; // cout<<cnt<<endl; } ll ans=0; ll n; ll tm[maxn<<2],num[maxn<<2],sum[maxn<<2]; comp x[maxn<<2],y[maxn<<2],z[maxn<<2]; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); cnt=0; ss(); memset(tm,0,sizeof tm); memset(num,0,sizeof num); memset(sum,0,sizeof sum); n=80000; int len=1; while(len<2*n) len<<=1; for (int i=0; i<n; i++) if (ok[i]) x[i]=comp(1,0); else x[i]=comp(0,0); for (int i=n+1; i<len; i++) x[i]=comp(0,0); fft(x,len,1); for (int i=0; i<len; i++) x[i]=x[i]*x[i]; fft(x,len,-1); for (int i=0; i<len; i++) num[i]=(int)(x[i].r+0.5); for (int i=0; i<=n; i++) if (ok[i]) num[i+i]--; for (int i=0; i<=n; i++) num[i]/=2; for (int i=0; i<=n; i++) x[i]=comp(num[i],0); for (int i=n+1; i<len; i++) x[i]=comp(0,0); for (int i=0; i<=n; i++) if (ok[i]) y[i]=comp(1,0); else y[i]=comp(0,0); for (int i=n+1; i<len; i++) y[i]=comp(0,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=0; i<len; i++) sum[i]=(int)(x[i].r+0.5); while(~scanf("%d",&n)) { ans=0; if (ok[n]) ans++;//a for (int i=0; i<cnt && prime[i]<=n; i++) for (int j=i; j<cnt&& prime[j]<n && prime[i]*prime[j]<=n; j++) if (prime[i]*prime[j]==n) ans++; //a*b for (int i=0; i<cnt && prime[i]<=n; i++) for (int j=i; j<cnt&& prime[i]*prime[j]<=n; j++) { int tp=n-prime[i]*prime[j]; if (ok[tp]) ans++;//a*b+c } for (int i=0; i<cnt && prime[i]<=n; i++) for (int j=i; j<cnt&& prime[i]*prime[j]<=n; j++) for (int k=j; k<cnt&& prime[i]*prime[j]*prime[k]<=n; k++) if (prime[i]*prime[j]*prime[k]==n) ans++;//a*b*c if (n%2==0 && ok[n/2]) ans++; ans+=num[n];//a+b ans%=mod; int tmp=0; for (int i=0; i<=n; i++) { if (ok[i]) { int tp=n-i; if (tp%2==0 && ok[tp/2]) tmp++; } } ans=(ans+(sum[n]+2*tmp)/3)%mod; if (n%3==0 && ok[n/3]) ans++; ans%=mod; //a+b+c printf("%d\n",ans); } return 0; }