【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]);
} //应该是标算了。。。。 

 

 

posted @ 2018-03-24 20:01  Newuser233  阅读(6)  评论(0编辑  收藏  举报