洛谷 P2312 解方程
阿新 • • 發佈:2019-01-24
首先,可以確定的是這題的做法就是暴力列舉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; }
根據的就是: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;
}