P2312 [NOIP2014 提高組] 解方程 題解
阿新 • • 發佈:2021-07-12
首先一看,我們可以發現a的範圍太大,只能寫高精度或者取模。
顯然,如果我們有 $ f(x) \equiv0 (mod p)$ ,當p為一個大質數時,有很大可能$f(x)=0$,我們可以注意到這也算是一個雜湊的思想。
寫高精度肯定是會超時的,只有從取模數這個方向考慮。
一、暴力
顯然,我們可以得到一種優雅的暴力的方法,直接列舉n,m,暴力計算$f(x)$的值,方法很簡單,還可以用快速冪稍微優化一點點。
時間複雜度約為$O(nmlogn)$
期望得分70-100,實際得分70
二、優化計算$f(x)$的值
這個代數式的值在幾百年前就已經被中國大數學家秦九韶研究過了(WC)(秦九韶演算法/霍納演算法)
使用這個演算法時,只需要使用n次乘法和加法即可得到代數式的值
時間複雜度約為$O(nm)$
期望得分100,實際得分70-100(卡常)
三、優化取模過程
這個就太牛逼了
對於一個多項式$f(x)$,若$ f(x) \equiv0 (mod p)$,則$ f(x%p) \equiv0 (mod p)$
那這就厲害了,我們只需要取一個小一點的模數,計算出p以內的所有$f(x)$值,利用結論直接推出大於p的$f(x)$是否與0同餘
但是當質數取得很大(如1e9+7)時,仍然有誤判的風險。
藉助雜湊的思想,我們去雙模數,如果雙模數不行,甚至可以取10個模數,100個模數!
那就幾乎不可能出現錯誤的情況了。
事實證明,當模數在$\sqrt{m}$左右的時候效率是最高的,這時候取10個模數比較保險。
時間複雜度約為$O(n\sqrt{m})$或$O(m)$,跑得飛快,一個點大約在20ms左右
程式碼:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 int mod[11]={0,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361}; 7 int n,m,a[1001][11],conf; 8 int b[2001][11],f[1000001],cnt; 9 void r(int x) 10 { 11 int k=1;char c=getchar(); 12 while(!isdigit(c)) 13 { 14 if(c=='-')k=-1; 15 c=getchar(); 16 } 17 while(isdigit(c)) 18 { 19 for(int i=1;i<=conf;i++) 20 { 21 a[x][i]=a[x][i]*10+c-'0'; 22 a[x][i]%=mod[i]; 23 } 24 c=getchar(); 25 } 26 for(int i=1;i<=conf;i++) 27 { 28 a[x][i]*=k; 29 a[x][i]=(a[x][i]%mod[i]+mod[i])%mod[i]; 30 // cout<<a[x][i]<<" "; 31 } 32 // cout<<endl; 33 } 34 inline int pw(int a,int b,int p)//a^b%p; 35 { 36 int ans=1,base=a; 37 while(b) 38 { 39 if(b&1) 40 { 41 ans*=base; 42 ans%=p; 43 } 44 b>>=1; 45 base*=base; 46 base%=p; 47 } 48 return ans; 49 } 50 int main() 51 { 52 // freopen("P2312_2.in","r",stdin); 53 conf=7; 54 cin>>n>>m; 55 n++; 56 for(int i=1;i<=n;i++)r(i); 57 for(int i=1;i<=conf;i++) 58 for(int j=0;j<mod[i];j++) 59 { 60 int now=0; 61 for(int k=1;k<=n;k++) 62 { 63 now+=a[k][i]*pw(j,k-1,mod[i]); 64 now%=mod[i]; 65 } 66 if(now==0) 67 { 68 // cout<<i<<" "<<j<<endl; 69 b[j][i]=1; 70 } 71 } 72 for(int i=1;i<=m;i++) 73 { 74 int flag=1; 75 for(int j=1;j<=conf;j++) 76 { 77 flag&=b[i%mod[j]][j]; 78 } 79 if(flag)f[++cnt]=i; 80 } 81 cout<<cnt<<endl; 82 for(int i=1;i<=cnt;i++) 83 cout<<f[i]<<endl; 84 }