题目描述
已知多项式方程:
a0+a1x+a2x^2+..+anx^n=0
求这个方程在[1, m ] 内的整数解(n 和m 均为正整数)
输入输出格式
输入格式:
输入文件名为equation .in。
输入共n + 2 行。
第一行包含2 个整数n 、m ,每两个整数之间用一个空格隔开。
接下来的n+1 行每行包含一个整数,依次为a0,a1,a2..an
输出格式:
输出文件名为equation .out 。
第一行输出方程在[1, m ] 内的整数解的个数。
接下来每行一个整数,按照从小到大的顺序依次输出方程在[1, m ] 内的一个整数解。
输入输出样例
2 10 1 -2 1
1 1
2 10 2 -3 1
2 1 2
2 10 1 3 2
0
说明
30%:0<n<=2,|ai|<=100,an!=0,m<100
50%:0<n<=100,|ai|<=10^100,an!=0,m<100
70%:0<n<=100,|ai|<=10^10000,an!=0,m<10000
100%:0<n<=100,|ai|<=10^10000,an!=0,m<1000000
分析:很久以前,阿贝尔证明过一元n次方程(n >= 5)是没有求根公式的,似乎只有30%的数据可以暴力求,怎么样才能AC呢?那就要做到缩小数据.可以注意到ai和m非常大,m似乎不能缩了,那ai怎么缩呢?注意到这是一个方程,那么就是取模运算了,因为要使方程的结果等于0,如果f(x) == 0,那么f(x) mod p == 0,但是逆定理不一定成立,但是如果多枚举几个p的话AC的可能性就比较大,少枚举会WA,枚举大了会TLE,这就是看脸的时候了,这个时候取一个10000左右的质数和一个比较大的质数 (大于m)即可,为什么要取质数呢?如果ai等于0那么就没有意义了.难道要一个一个枚举x吗?这样未免太慢了,注意到如果f(x) mod p == 0,那么f(x + p) mod p = f(x) mod p + p mod p == 0,所以我们在x的基础上不断的加p即可.在计算的时候一旦有可能超过p,就mod.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; char s[10010]; int p[3], n, m; long long a[3][105]; bool can[1000010]; void quyu(char *s1, int k) { bool fu = false; int len = strlen(s1), i; for (int j = 1; j <= 2; j++) { i = 0; if (s1[0] == ‘-‘) { fu = true; i = 1; } for (; i < len; i++) a[j][k] = (a[j][k] * 10LL % p[j] + s[i] - ‘0‘) % p[j]; if (fu == true) a[j][k] = p[j] - a[j][k]; } } int panduan(int x, int k) { long long ans = 0, b = 1; for (int i = 0; i <= n; i++) { ans = (ans + 1LL * a[k][i] * b) % p[k]; b = 1LL * b * x % p[k]; } return ans % p[k]; } int main() { p[1] = 67891, p[2] = 1000000207; //两个质数 scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) can[i] = false; for (int i = 0; i <= n; i++) { scanf("%s", s); quyu(s, i); } for (int i = 1; i <= p[1]; i++) { if (panduan(i, 1) != 0) continue; for (int j = i; j <= m; j += p[1]) if (panduan(j, 2) == 0) can[j] = true; } int ans = 0; for (int i = 1; i <= m; i++) if (can[i]) ans++; printf("%d\n", ans); for (int i = 1; i <= m; i++) if (can[i]) printf("%d\n", i); return 0; }