NOIP2014/洛谷P2312 解方程
题目描述
已知多项式方程:
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 ] 内的一个整数解。
输入输出样例
输入样例#1: 复制
2 10 1 -2 1
输出样例#1: 复制
1 1
输入样例#2: 复制
2 10 2 -3 1
输出样例#2: 复制
2 1 2
输入样例#3: 复制
2 10 1 3 2
输出样例#3: 复制
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
分析
对于30%的数据,用公式解就可以了。
对于50%的数据,高精度?
对于100%,m的范围不大,在这个范围内枚举,计算最后的值是否为0。要用到秦九韶算法,这样只需要做n次乘法,n次加法。但是a很大,高精度也很难表示怎么办?如果方程左面的值为0,那么方程两边同时mod一个值还是成立的。如果只mod一个值的话,答案很有可能不是正确的,因为如果这个值要是mod这个值的倍数的话,mod的值也是0。我们可以多mod几个值,那么这个数就必须满足是这几个值的最小公倍数的倍数才能在mod之后都为0,否则的话这个数就是0。但是前一种情况机率还是很小的,因为如果模数都取质数的话最小公倍数就是这几个数的乘积,还是很大的。但是这样的话时间复杂度是O(n*m)的,会超时的。如果一个数x mod p !=0 的话,那么x+p mod p!=0,这样就可以少计算一些无用的数。这样的话应该能通过官方数据。
关于秦九韶算法a[0]是否要乘以x,从公式上来看是不需要的,但是如果到最后结果如果是0的话就算乘以x之后还是0,对结果没有什么太大的影响,而且好记,代码短(lazy!)。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N=1000010; const int mo1=99991; const int mo2=998244353; const int mo3=1e9+7; int n,m,cnt,a[N],b[N],c[N],ans[N]; bool vis[N]; inline void read(int i){ ll x1=0,x2=0,x3=0,f=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){ x1=(x1*10+ch-‘0‘)%mo1; x2=(x2*10+ch-‘0‘)%mo2; x3=(x3*10+ch-‘0‘)%mo3; ch=getchar(); } a[i]=x1*f;b[i]=x2*f;c[i]=x3*f; } inline bool calc1(int x){ ll sum=0; for(int i=n;i>=0;--i) sum=((a[i]+sum)*x)%mo1; return !sum; } inline bool calc2(int x){ ll sum1=0,sum2=0; for(int i=n;i>=0;--i){ sum1=((b[i]+sum1)*x)%mo2; sum2=((c[i]+sum2)*x)%mo3; } return (!sum1)&&(!sum2); } int main(){ scanf("%d%d",&n,&m); for(int i=0;i<=n;++i) read(i); for(int i=1;i<=mo1;++i) if(calc1(i)){ for(int j=i;j<=m;j+=mo1) if(calc2(j)) vis[j]=1; } for(int i=1;i<=m;++i) if(vis[i]) ans[++cnt]=i; printf("%d\n",cnt); for(int i=1;i<=cnt;++i) printf("%d\n",ans[i]); return 0; }