BZOJ 2742: [HEOI2012]Akai的数学作业

2742: [HEOI2012]Akai的数学作业

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 535  Solved: 226
[Submit][Status][Discuss]

Description

这里是广袤无垠的宇宙这里是一泻千里的银河
这里是独一无二的太阳系
这里是蔚蓝色的地球
这里,就是这里,是富饶的中国大陆!
这里是神奇的河北大地
这里是美丽的唐山
这里是神话般的唐山一中
这里是Akai曾经的教室
黑板上还留有当年Akai做过的数学作业,其实也并不是什么很困难的题目:
 
给出一个一元n次方程:
a0 + a1x + a    2   x2 ++ anxn= 0
求此方程的所有有理数解。
 
Akai至今还深刻记得当年熬夜奋战求解的时光
他甚至还能记得浪费了多少草稿纸
但是却怎么也想不起来最后的答案是多少了
你能帮助他么?

Input

第一行一个整数n。第二行n+1个整数,分别代表a    0 a n

Output

第一行输出一个整数t,表示有理数解的个数
接下来t行,每行表示一个解
解以分数的形式输出,要求分子和分母互质,且分母必须是正整数特殊的,如果这个解是一个整数,那么直接把这个数输出
等价的解只需要输出一次
所有解按照从小到大的顺序输出

Sample Input

3
-24 14 29 6

Sample Output

3
-4
-3/2
2/3

HINT

【数据范围】

对于30%的数据,n<=10

对于100%的数据,n <= 100,|a i| <= 2*10^7,an≠ 0

Source

[Submit][Status][Discuss]

 

好神的一道HEOI题。

据LH讲,有个定理叫做多项式高斯引理什么的,大概就是讲,复数域下的一个关于$x$的$n$次多项式$f(x)=a_{0}+a_{1}x+a_{2}x^{2}+a_{3}x^{3}+...+a_{n}x^{n}$一定可以分解成$n$个含$x$的一次多项式相乘,即$f(x)$一定存在一种形如$f(x)=\prod{(b_{i}x+c_{i})}$的表示,其中每个式子都会产生一个复数域下的根(当然,这些根有可能重复)。这道题叫我们只用考虑有理数根,所以可以把式子改写为$f(x)=g(x)*\prod{(b_{i}x+c_{i})}$的样子,其中g(x)是一个关于$x$的多项式,包含了所有的非有理数根,剩下的部分就表示了所有的有理数根。发现每个有理数根都能表示成$x_{i}=\frac{c_{i}}{b_{i}}$,然后不难发现$f(x)=\sum_{i=0}^{n}{a_{i}x^{i}}$中的$a_{0}$包含了所有的$c_{i}$,而$a_{n}$包含了有所的$b_{i}$,所以对于所有的合法有理数根$x_{i}=\frac{c_{i}}{b_{i}}$,$c_{i}$一定是$a_{0}$的约数,$b_{i}$一定是$a_{n}$的约数。所以可以先处理出$a_{0}$和$a_{n}$的所有约数,然后暴力枚举$b_{i}$和$c_{i}$,$O(N)$check是否合法即可。check的方式是,对于$x=\frac{p}{q}$,$f(x)=\sum_{i=0}^{n}{a_{i}p^{i}q^{n-i}}$,在模意义下检查是否为$0$即可。

 

  1 #include <bits/stdc++.h>
  2 
  3 template <class T>
  4 T gcd(T a, T b)
  5 {
  6     return b ? gcd(b, a % b) : a;
  7 }
  8 
  9 typedef long long lnt;
 10 
 11 const int mxn = 105;
 12 const int mxm = 500005;
 13 const lnt mod = 1000000007;
 14 
 15 int n, s[mxn];
 16 
 17 struct number
 18 {
 19     int a, b, f;    // ans = a / b
 20 
 21     number(void) {};
 22     number(int x, int y, int g = 1)
 23         : a(x), b(y), f(g) {};
 24 
 25     void print(void)
 26     {
 27         if (f == -1)
 28             putchar('-');
 29         if (a % b)
 30             printf("%d/%d\n", a, b);
 31         else
 32             printf("%d\n", a / b);
 33     }
 34 }ans[mxm]; int tot;
 35 
 36 bool cmp(const number &A, const number &B)
 37 {
 38     if (A.f == -1 && B.f == +1)
 39         return true;
 40     if (A.f == +1 && B.f == -1)
 41         return false;
 42     if (A.f == +1 && B.f == +1)
 43         return 1LL * A.a * B.b < 1LL * B.a * A.b;
 44     if (A.f == -1 && B.f == -1)
 45         return 1LL * A.a * B.b > 1LL * B.a * A.b;
 46 }
 47 
 48 void leadingZeros(void)
 49 {
 50     int cnt = 0;
 51 
 52     while (!s[cnt])
 53         ++cnt;
 54 
 55     if (cnt)
 56     {
 57         n = n - cnt;
 58     
 59         for (int i = 0; i <= n; ++i)
 60             s[i] = s[i + cnt];
 61     
 62         ans[tot++] = number(0, 1);
 63     }
 64 }
 65 
 66 int divA[mxm], sizA;
 67 int divB[mxm], sizB;
 68 
 69 void divide(int x, int *div, int &siz)
 70 {
 71     if (x < 0)x = -x;
 72 
 73     siz = 0;
 74 
 75     int t = int(sqrt(x));
 76 
 77     for (int i = 1; i <= t; ++i)
 78         if (x % i == 0)
 79         {
 80             div[siz++] = i;
 81             div[siz++] = x / i;
 82         }
 83 
 84     if (t * t == x)--siz;
 85 }
 86 
 87 int powA[mxn];
 88 int powB[mxn];
 89 
 90 void check(lnt a, lnt b, lnt f)
 91 {
 92     powA[0] = powB[0] = 1LL;
 93 
 94     for (int i = 1; i <= n; ++i)
 95     {
 96         powA[i] = (powA[i - 1] * a) % mod;
 97         powB[i] = (powB[i - 1] * b) % mod;
 98     }
 99 
100     lnt sum = 0, tmp;
101 
102     for (int i = 0; i <= n; ++i)
103     {
104         tmp = s[i];
105         tmp = (tmp * powA[i]) % mod;
106         tmp = (tmp * powB[n - i]) % mod;
107 
108         if (i & 1)tmp = (tmp * f + mod) % mod;
109 
110         sum = (sum + tmp) % mod;
111     }
112 
113     if (sum == 0)ans[tot++] = number(a, b, f);
114 }
115 
116 signed main(void) 
117 {
118     scanf("%d", &n);
119 
120     for (int i = 0; i <= n; ++i)
121         scanf("%d", s + i);
122 
123     leadingZeros();
124 
125     divide(s[0], divA, sizA);
126     divide(s[n], divB, sizB);
127 
128     for (int i = 0; i < sizA; ++i)
129         for (int j = 0; j < sizB; ++j)
130         {
131             int a = divA[i];
132             int b = divB[j];
133 
134             if (gcd(a, b) == 1)
135             {
136                 check(a, b, +1);
137                 check(a, b, -1);
138             }
139         }
140 
141     std::sort(ans, ans + tot, cmp);
142 
143     printf("%d\n", tot);
144 
145     for (int i = 0; i < tot; ++i)
146         ans[i].print();
147 }

 

@Author: YouSiki

posted @ 2017-02-16 08:30  YouSiki  阅读(843)  评论(1编辑  收藏  举报