P2312 [NOIP2014 提高组] 解方程 题解
首先一看,我们可以发现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 }
使用秦九韶算法的代码:
1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 #include<algorithm>
5 using namespace std;
6 int mod[11]={0,1009,1013,1019,1021,1031,1033,1039,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=6;
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=a[n][i];
61 for(int k=n;k>1;k--)
62 {
63 now*=j;
64 now%=mod[i];
65 now+=a[k-1][i];
66 now%=mod[i];
67 }
68 if(now==0)
69 {
70 // cout<<i<<" "<<j<<endl;
71 b[j][i]=1;
72 }
73 }
74 for(int i=1;i<=m;i++)
75 {
76 int flag=1;
77 for(int j=1;j<=conf;j++)
78 {
79 flag&=b[i%mod[j]][j];
80 }
81 if(flag)f[++cnt]=i;
82 }
83 cout<<cnt<<endl;
84 for(int i=1;i<=cnt;i++)
85 cout<<f[i]<<endl;
86 }
本文来自博客园,作者:lei_yu,转载请注明原文链接:https://www.cnblogs.com/lytql/p/15003363.html