【BZOJ2742】【HEOI2012】Akai的数学作业 [数论]
Akai的数学作业
Time Limit: 10 Sec Memory Limit: 128 MB[Submit][Status][Discuss]
Description
这里是广袤无垠的宇宙这里是一泻千里的银河,这里是独一无二的太阳系,这里是蔚蓝色的地球
这里,就是这里,是富饶的中国大陆!
这里是神奇的河北大地,这里是美丽的唐山,这里是神话般的唐山一中,这里是Akai曾经的教室
黑板上还留有当年Akai做过的数学作业,其实也并不是什么很困难的题目:
“
给出一个一元n次方程:
a0 + a1x + a 2 x2 +…+ anxn= 0
求此方程的所有有理数解。
”
Akai至今还深刻记得当年熬夜奋战求解的时光,他甚至还能记得浪费了多少草稿纸。
但是却怎么也想不起来最后的答案是多少了,你能帮助他么?
Input
Output
Sample Input
-24 14 29 6
Sample Output
-4
-3/2
2/3
HINT
对于30%的数据,n <= 10
对于100%的数据,n <= 100,|a i| <= 2*10^7,an ≠ 0
Main idea
给出一个一元n次方程:A0+A1*x+A2*x^2+…+An*x^n,求出这个方程的所有有理数解。
Solution
这必然是一道数论题。首先我们发现了题目的一个非常重要的特征:求的是有理数解。
立马想到了分解因式,因为要的是有理数解,所以原方程肯定可以表示成:
x就是q/p。
然后再来思考一下。我们先从最简单的情况开始处理,也就是A0≠0,An≠0的情况。
显然可以知道p一定是An分出来的,q一定是A0分出来的,那么一定有p是An的约数,q是A0的约数,那么这时候所有的情况就应该是
仔细推一下式子,发现了一个规律:几个约数相乘的情况所表达出的集合和不考虑相乘情况的集合是一样的!那么处理就简单了很多。
由于可能有前几项系数=0的情况,所以我们从A0的想法出发,找到第一个系数非0的项将这一项的约数存下来(如果不是A0的话则在答案中加一个0),然后从后往前找找到第一个非0的存下它的约数。然后O(约数个数)^2枚举任意两种情况的q/p放到原式里面判断(答案有可能是负数所以还要检查一下-q/p可不可行)然后在检查的时候发现了一个问题,数字要么精度误差过大要么就是爆出int范围了,我们想到了通分,分子分母同乘上p^n,避免了精度问题。
以n=3的举个例子:将原式
转化为
然后我们就可以不管分母了,用这样的方法解决了精度问题。那么怎么解决爆int范围的问题呢?我们发现,在每次操作的时候都对一个质数取模的话错解的几率不是非常大,那么我们就可以大胆地取模几个质数来判断,如果不放心可以多取模几个。
BearChild发现了一个神奇的质数:50033(如果使用这个质数的话是不需要用其余几个质数判断的)。
这样进行累加和是否为0的判断,可行的话将每个答案存下来,然后sort一遍输出即可。
Code
1 #include<iostream>
2 #include<algorithm>
3 #include<cstdio>
4 #include<cstring>
5 #include<cstdlib>
6 #include<cmath>
7 using namespace std;
8
9 const int ONE=501;
10 const int MOD=50033;
11
12 int n;
13 int A[ONE];
14 int divisor[ONE][3],num[3];
15 int p[ONE];
16 int q[ONE];
17 int Repeat[ONE];
18 int cnt;
19
20 struct power
21 {
22 int l,r;
23 int value;
24 }a[ONE];
25
26 int cmp(const power &a,const power &b)
27 {
28 return (double)a.l/a.r < (double)b.l/b.r;
29 }
30
31 int get()
32 {
33 int res,Q=1; char c;
34 while( (c=getchar())<48 || c>57)
35 if(c=='-')Q=-1;
36 if(Q) res=c-48;
37 while((c=getchar())>=48 && c<=57)
38 res=res*10+c-48;
39 return res*Q;
40 }
41
42 int gcd(int a,int b)
43 {
44 int r=a%b;
45 while(r)
46 {
47 a=b;
48 b=r;
49 r=a%b;
50 }
51 return b;
52 }
53
54
55 void Deal(int x,int PD)
56 {
57 for(int i=1;i<=x;i++)
58 {
59 if(!(x%i)) divisor[++num[PD]][PD]=i;
60 }
61 }
62
63 int Check(int x,int y)
64 {
65 p[0]=q[0]=1;
66 for(int i=1;i<=n;i++)
67 {
68 p[i]=(long long)p[i-1]*y % MOD; q[i]=(long long)q[i-1]*x % MOD;
69 }
70
71 long long res=0;
72 for(int i=0;i<=n;i++)
73 {
74 res=(res+(long long)p[n-i]*q[i]*A[i]) % MOD;
75 }
76
77 if(!res) return 1;
78 return 0;
79 }
80
81 int main()
82 {
83 n=get();
84 for(int i=0;i<=n;i++) A[i]=get();
85 if(A[0]==0)
86 {
87 a[++cnt].l=0; a[cnt].r=1;
88 }
89 for(int i=0;i<=n;i++)
90 {
91 if(A[i])
92 {
93 Deal(abs(A[i]),1);
94 break;
95 }
96 }
97
98 for(int i=n;i>=0;i--)
99 {
100 if(A[i])
101 {
102 Deal(abs(A[i]),2);
103 break;
104 }
105 }
106
107 for(int i=1;i<=num[1];i++)
108 for(int j=1;j<=num[2];j++)
109 {
110 int x=divisor[i][1];
111 int y=divisor[j][2];
112 if(gcd(x,y)!=1) continue;
113 if(Check(x,y))
114 {
115 a[++cnt].l=x;
116 a[cnt].r=y;
117 }
118 if(Check(-x,y))
119 {
120 a[++cnt].l=-x;
121 a[cnt].r=y;
122 }
123 }
124
125 sort(a+1,a+cnt+1,cmp);
126 printf("%d\n",cnt);
127 for(int i=1;i<=cnt;i++)
128 {
129 if(a[i].r==1) printf("%d\n",a[i].l);
130 else printf("%d/%d\n",a[i].l,a[i].r);
131 }
132
133 }