【NOIP2014 Day2 T3解方程】很神奇的分块枚举大法
我们可以想到,这道题ai模一个大质数之后,原来能够满足方程成立的解x仍然能够成立,但是也会出现一些原来不能使得方程成立的x极小概率使方程成立。由于我们模一个比较优秀的大质数(比如不是常被卡的1e9+7虽然我写的仍然是1e9+7,但是想到不这样,这道题就没法做了,可见ccf还是挺良心的)
第一种做法,直接枚举x进行判断,写起来十分简便容易,时间复杂度O(nm)能够get到70分
第二种做法,不知道标算是否是这样的,但且当其为标算来看,思想类通分块BSGS(传送门: http://www.newuser.top/2018/03/20/poj2417-discrete-logging%EF%BC%88%E9%AB%98%E6%AC%A1%E5%90%8C%E4%BD%99%E6%96%B9%E7%A8%8B%EF%BC%89/)。
我们可以模过大质数后再模一个质数,得到b1,b2,b3.....
bi=ai (mod p)
然后枚举[0,p-1]中的x,不会超过n个。
之后再枚举[1,m]的x中mod p 后满足上面的方程的 x在代入模大质数后的方程里面进行判断。
时间复杂度O(np+n^2(m/p)),可知当p=sqrt(nm)时,时间复杂度(n sqrt(nm) )
talk is cheap , show me code:
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<map> #include<cmath> #define dzs (1000000007) #define ll long long using namespace std; ll p; ll a[105],n,m,b[105]; void R(ll x) { bool mark=0; a[x]=b[x]=0; char ch=getchar(); while((ch>'9'||ch<'0')&&(ch!='-')) ch=getchar(); if(ch=='-') mark=1; else a[x]=b[x]=ch-'0'; ch=getchar(); while(ch>='0'&&ch<='9'){ a[x]=((a[x]<<3)+(a[x]<<1)+ch-'0')%dzs; b[x]=((b[x]<<3)+(b[x]<<1)+ch-'0')%p; ch=getchar(); } if(mark) {a[x]=-a[x]; b[x]=-b[x];} } bool dra(ll x) { ll rez=0; for(ll i=n;i>=0;i--) { rez=(rez*x+a[i])%dzs;//qjz dalao } if(!rez) return true; else return false; }//寻找到比较优秀的小质数 bool drb(ll x) { ll rez=0; for(ll i=n;i>=0;i--) { rez=(rez*x+b[i])%p;//qjz dalao } if(!rez) return true; else return false; } ll findzs(ll from) { if(from==2) return from; for(;from<=m;from++) { for(ll i=2;i<=(ll)sqrt(from);i++) { if(from%i==0) goto aha; } return from; aha:continue; } from--; return from; } ll ans[10000005],tot; bool sz[10000005]; int main() { scanf("%lld%lld",&n,&m); p=sqrt(n*m); p=findzs(p); for(ll i=0;i<=n;i++) { R(i); } for(ll i=0;i<=p-1;i++) { if(drb(i)) sz[i]=1; } for(ll i=1;i<=m;i++) { if(sz[i%p]) { if(dra(i)) { ans[++tot]=i; } } } printf("%lld\n",tot); for(ll i=1;i<=tot;i++) printf("%lld\n",ans[i]); } //应该是标算了。。。。