洛谷 P2312 解方程
首先,可以确定的是这题的做法就是暴力枚举x,然后去计算方程左边与右边是否相等。
但是noip的D2T3怎么会真的这么简单呢?卡常卡的真是熟练 你需要一些优化方法。
首先可以用秦九韶公式优化一下方程左边的计算方法:
左边=(((..(a[n]*x)+a[n-1])*x+..+a[1])*x+a[0]
然后我就试着直接去算:
#include<cstdio> typedef long long LL; LL a[110],ans[1001000]; LL n,m,aa; int main() { LL i,j; scanf("%lld%lld",&n,&m); for(i=0;i<=n;i++) scanf("%lld",&a[i]); for(i=n;i>=1;i--) for(j=1;j<=m;j++) { ans[j]+=a[i]; ans[j]*=j; } for(i=1;i<=m;i++) ans[i]+=a[0]; for(i=1;i<=m;i++) if(ans[i]==0) aa++; printf("%lld\n",aa); for(i=1;i<=m;i++) if(ans[i]==0) printf("%lld\n",i); return 0; }
然后就Wa(30)掉了...没看清数据范围(ai最长有10000位,而不是最大10000)
也许你会想到高精度运算,但是很容易发现,这题的数据范围太大,直接高精度暴力算太慢。
此时有一个小trick:对于每个a[n],读入的时候对某一些大质数取模。对于每个枚举出的x,就用取模过的a[i]去算。如果用对好几组对不同质数取模得到的a[i]算都能得到0,那么就认为x是合法的。
直觉上可能觉得很容易被卡掉?但事实上一点也不容易被卡....好像还是正解..
那么对于负的a[i]怎么去取模呢?很简单,读入的时候看一下符号位,然后按照正数的方式取模(每读入一位将当前余数乘10再加当前位再取模)。处理完整个数后,如果记录的符号位是负数,那么就将余数变为模数-余数。
然后我去交...然后就T(70)掉了...还是太慢。(而且O2都救不了我...)
//#pragma GCC optimize(2) #include<cstdio> #include<cstring> typedef long long LL; LL a[5][110],ans[5][1001000]; LL prime[]={179424629,179424667,179424671,179424673,179424691}; LL pnum=5,n,m,aa; char c; bool nok[1010000]; int main() { //freopen("testdata.in","r",stdin); //freopen("testdata.ss","w",stdout); LL i,j,i1,fl,sl,p; scanf("%lld%lld",&n,&m); c=getchar(); for(i=0;i<=n;i++) { while(!((c>='0'&&c<='9')||c=='-')) c=getchar(); if(c=='-') fl=1,c=getchar(); else fl=0; for(j=0;j<pnum;j++) { for(i1=fl;c>='0'&&c<='9';i1++) { a[j][i]=(a[j][i]*10+c-'0'); while(a[j][i]>=prime[j]) a[j][i]-=prime[j]; //ans[j][i]-=prime[j] c=getchar(); } if(fl) a[j][i]=prime[j]-a[j][i]; } } for(p=0;p<pnum;p++) { for(j=1;j<=m;j++) if(!nok[j]) for(i=n;i>=1;i--) { ans[p][j]=(ans[p][j]+a[p][i])*j%prime[p]; } for(j=1;j<=m;j++) if(!nok[j]) { ans[p][j]+=a[p][0]; while(ans[p][j]>=prime[p]) ans[p][j]-=prime[p]; } for(j=1;j<=m;j++) if(ans[p][j]!=0) nok[j]=1; } for(i=1;i<=m;i++) if(!nok[i]) aa++; printf("%lld\n",aa); for(i=1;i<=m;i++) if(!nok[i]) printf("%lld\n",i); return 0; }
卡了很久的常之后,我放弃了,去看了题解。
原来这个算法是可以接着优化的:http://www.cnblogs.com/NaVi-Awson/p/7566889.html
根据的就是:f(x)≡0(modp),则f(x+p)≡0(modp)
这样子可以快速过滤掉一些m。(怎么觉得是卡常呢...)
然后,我又去交..又T(70)了..
原因:质数选的太大,这样是不能筛掉什么m的
#pragma GCC optimize(2) #include<cstdio> #include<cstring> typedef long long LL; LL a[5][110],ans[5][1001000]; LL prime[]={179424629,179424667,179424671,179424673,179424691}; LL pnum=5,n,m,aa; char c; bool nok[1010000]; int main() { //freopen("testdata.in","r",stdin); //freopen("testdata.ss","w",stdout); LL i,j,i1,fl,sl,p,k; scanf("%lld%lld",&n,&m); c=getchar(); for(i=0;i<=n;i++) { while(!((c>='0'&&c<='9')||c=='-')) c=getchar(); if(c=='-') fl=1,c=getchar(); else fl=0; for(j=0;j<pnum;j++) { for(i1=fl;c>='0'&&c<='9';i1++) { a[j][i]=(a[j][i]*10+c-'0'); while(a[j][i]>=prime[j]) a[j][i]-=prime[j]; //ans[j][i]-=prime[j] c=getchar(); } if(fl) a[j][i]=prime[j]-a[j][i]; } } for(p=0;p<pnum;p++) { for(j=1;j<=m;j++) if(!nok[j]) { for(i=n;i>=1;i--) ans[p][j]=(ans[p][j]+a[p][i])*j%prime[p]; ans[p][j]+=a[p][0]; while(ans[p][j]>=prime[p]) ans[p][j]-=prime[p]; if(ans[p][j]!=0) for(k=j;k<=m;k+=prime[p]) nok[k]=1; } } for(i=1;i<=m;i++) if(!nok[i]) aa++; printf("%lld\n",aa); for(i=1;i<=m;i++) if(!nok[i]) printf("%lld\n",i); return 0; }
改进:可以用比较小的质数筛掉大部分m,然后用大质数筛掉剩下(也许存在的)不合法的m。
AC代码:
//#pragma GCC optimize(2) #include<cstdio> #include<cstring> typedef long long LL; LL a[5][110],ans[5][1001000]; LL prime[]={81799,81817,179424671,179424673,179424691}; LL pnum=5,n,m,aa; char s[10100]; bool nok[1010000]; int main() { //freopen("testdata.in","r",stdin); //freopen("testdata.ss","w",stdout); LL i,j,i1,fl,sl,p,k; scanf("%lld%lld",&n,&m); // c=getchar(); // for(i=0;i<=n;i++) // { // while(!((c>='0'&&c<='9')||c=='-')) c=getchar(); // if(c=='-') // fl=1,c=getchar(); // else // fl=0; // for(j=0;j<pnum;j++) // { // for(i1=fl;c>='0'&&c<='9';i1++) // { // a[j][i]=(a[j][i]*10+c-'0'); // while(a[j][i]>=prime[j]) a[j][i]-=prime[j]; // //ans[j][i]-=prime[j] // c=getchar(); // } // if(fl) a[j][i]=prime[j]-a[j][i]; // } // } for(i=0;i<=n;i++) { scanf("%s",s); if(s[0]=='-') fl=1; else fl=0; sl=strlen(s); for(j=0;j<pnum;j++) { for(i1=fl;i1<sl;i1++) a[j][i]=(a[j][i]*10+s[i1]-'0')%prime[j]; if(fl) a[j][i]=prime[j]-a[j][i]; } } for(p=0;p<pnum;p++) { for(j=1;j<=m;j++) if(!nok[j]) { for(i=n;i>=1;i--) ans[p][j]=(ans[p][j]+a[p][i])*j%prime[p]; ans[p][j]+=a[p][0]; while(ans[p][j]>=prime[p]) ans[p][j]-=prime[p]; if(ans[p][j]!=0) for(k=j;k<=m;k+=prime[p]) nok[k]=1; } } for(i=1;i<=m;i++) if(!nok[i]) aa++; printf("%lld\n",aa); for(i=1;i<=m;i++) if(!nok[i]) printf("%lld\n",i); return 0; }