题意:求高次方程的解及其个数。其中
1°
我们知道,高次方程是没有求根公式的。但是利用逆向思维,我们可以进行“试根法”,因为题目中给出了所求根的范围。但是多项式系数过于吓人,达到了sxbk的1e10000.longlong显然盛不下。只能看做字符串处理。然而即使是处理成字符串,我们也不可能真的去乘这么多。
2°
考虑取膜。我们把多项式系数进行取膜,它的相对效果和不取膜是一样的。(想一想,为什么)
除了对系数取膜,我们还可以考虑对x取膜。
- 如果 X 真的是一个根,那么取模后肯定是 0;但反之则是不确定。
- 逆否命题:如果取模之后不是 0,那么 X 肯定不是一个根。
- 我们就利用上面的这条性质来判断即可。- 考虑一个较小的模数 p。
- 如果对于 0<x<p 来说,代入 x 计算后不是 0,则那么对于 x+p,
x+2p, x+3p...,这些数代入计算后都不可能为 0。
- 所以我们只需要验证[1,p)之间的数,剩下的可以直接推得。
- 时间复杂度: O(TMN)。其中 T 为模数的个数 P。
我们当然还不能仅用一个数取膜,需要多个质数(通常用质数)来提高正确率,这并不是一个精确的算法,但在大多数情况下成立。
3°
难道在x比较小的时候,我们计算这个多项式的值也需要暴力搞嘛?
我们智慧的先人秦九韶早就计算了一种计算多项式的简化方法,复杂度O(n)
(你可以在高中数学必修3中学到它)
它大概长这样:
它的代码大概长这样:
1 bool check(int x,int mo)
2 {
3 ll num=0;
4 for(int i=n+1;i>=1;i--)
5 num=(num*x+a[i][mo])%prime[mo];
6 return num==0;
7 }
于是我们就可以 快速求解多项式的值了。
至此,本题就结束了=w=。
复杂度O(TMN),T为选取模数的个数。
Code
1 #include<cstdio>
2 #include<algorithm>
3 #include<cstring>
4
5 using namespace std;
6 typedef long long ll;
7
8 int n,m,ans;
9 char op[20000];
10 int a[500][10];
11 bool vis[2000000];
12 int prime[10]={0,10691,14551,21871,39521,24179};
13
14 void read(int pos)
15 {
16 bool flag=0;
17 int st=0;
18 scanf("%s",op+1);
19 if(op[1]=='-') flag=1,st=2;
20 else st=1;
21 for(int i=st;i<=strlen(op+1);i++)
22 for(int j=1;j<=5;j++)
23 a[pos][j]=(a[pos][j]*10+op[i]-'0')%prime[j];
24 if(flag)
25 for(int j=1;j<=5;j++)
26 a[pos][j]=(prime[j]-a[pos][j])%prime[j];
27 }
28
29 bool check(int x,int mo)
30 {
31 ll num=0;
32 for(int i=n+1;i>=1;i--)
33 num=(num*x+a[i][mo])%prime[mo];
34 return num==0;
35 }
36
37 int main()
38 {
39 scanf("%d%d",&n,&m);
40 for(int i=1;i<=n+1;i++) read(i);
41 for(int j=1;j<=5;j++)
42 {
43 int limit=min(prime[j]-1,m);
44 for(int i=1;i<=limit;i++)
45 if(!check(i,j))
46 for(int k=i;k<=m;k+=prime[j])
47 vis[k]=1;
48 }
49 for(int i=1;i<=m;i++)
50 if(!vis[i]) ans++;
51 if(!ans){printf("0");return 0;}
52 else printf("%d\n",ans);
53 for(int i=1;i<=m;i++)
54 if(!vis[i]) printf("%d\n",i);
55 return 0;
56 }
View Code
* 开始交的时候脑子抽了以为1e6是100000于是愉快地RE了三个点233
独立意志与自由思想是必须争的,且须以生死力争。