刷题总结——解方程(NOIP2014)
题目:
题目描述
已知多项式方程:
a0+a1x+a2x2+…+anxn=0
求这个方程在[1,m]内的整数解(n 和 m 均为正整数)。
输入格式
输入共 n+2 行。
第一行包含 2 个整数 n、m,每两个整数之间用一个空格隔开。
接下来的 n+1 行每行包含一个整数,依次为 a0,a1,a2, … ,an 。
输出格式
第一行输出方程在[1,m]内的整数解的个数。
接下来每行一个整数,按照从小到大的顺序依次输出方程在[1,m]内的一个整数解。
样例数据 1
题解
这道题不得不说思想很巧···以前已知没有遇到过···
首先,如果等式两边模上一个数后依然为0那么原来的等式是有可能成立的··因此我们可以取几个质数然后看每次算完后模这几个质数下来的答案是否都为0,如果是的话说明原来等式可能成立(概率很大)
但是如果这样从1——m一个一个枚举暴力算还是会超时的···
我们还可以发现一个性质··就是如果一个x带入等式模质数p为0,那么x+k*p带入肯定等式模质数p肯定也一定为0··因此我们枚举小于质数的数计算即可·····这样复杂度就是k*p*n的,其中p为最大质数的大小··k为选择的质数的数量····注意质数选小一点···10000左右即可
但这道题我写出来常树很大··怎么优化都不能过bzoj··只能过自己学校的···如果要参考我代码的同学还是算了吧···
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> using namespace std; const int N=1e4+5; const int M=105; const int P=1e6+5; int pri[6]={0,11261,19997,22877,21893,14843}; int a[M][10],n,m,pre[30005][10],jud[10][30005],ans[P],cnt=0; char s[N]; inline int R() { char c;int f=0; for(c=getchar();c<'0'||c>'9';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0'; return f; } inline bool check(int op,int x) { long long ans=0; for(register int i=0;i<=n;i++) ans=(ans+pre[op][i]*a[i][op])%pri[op]; if(ans<0) ans+=pri[op]; return ans==0; } int main() { //freopen("a.in","r",stdin); n=R(),m=R(); for(register int i=0;i<=n;i++) { bool flag=false; scanf("%s",s+1);int len=strlen(s+1); if(s[1]=='-') flag=true; else for(int j=1;j<=5;j++) a[i][j]=s[1]-'0'; for(register int j=2;j<=len;j++) for(register int k=1;k<=5;k++) a[i][k]=(a[i][k]*10%pri[k]+s[j]-'0')%pri[k]; if(flag) for(int j=1;j<=5;j++) a[i][j]=-a[i][j]; } for(register int i=1;i<=5;i++) for(register int j=1;j<pri[i];j++) { pre[i][0]=1; for(int k=1;k<=n;k++) pre[i][k]=pre[i][k-1]*j%pri[i]; if(check(i,j)) jud[i][j]=true; } for(register int i=1;i<=m;i++) { bool flag=true; for(register int j=1;j<=5;j++) if(!jud[j][i%pri[j]]) { flag=false;break; } if(flag) ans[++cnt]=i; } printf("%d\n",cnt); for(register int i=1;i<=cnt;i++) printf("%d\n",ans[i]); return 0; }