1. 程式人生 > >洛谷 P2312 解方程

洛谷 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;
}
卡了很久的常之後,我放棄了,去看了題解。

根據的就是: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;
}