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 }
View Code

 

posted @ 2021-07-12 18:42  lei_yu  阅读(62)  评论(0编辑  收藏  举报