zoukankan      html  css  js  c++  java
  • bzoj-2219 数论之神

    题意:

    求方程X^A = B(mod 2*K + 1)

    X ∈[0, 2K] 内的解的个数;


    题解:
    一道数论的好题。

    涉及知识点大概有:Crt推论。BSGS,EXGCD,原根与指标;

    这道题的主要问题在于两点:

    第一点:取模数不是质数,无法利用通常的方式解方程。

    可是有中国剩余定理这个东西,定理的推论告诉我们:

    一个取模数互质的同余方程组(未必线性),组合起来之后。这个同余方程解的个数为各方程解的个数的乘积

    (组合起来的方程的取模数为全部数的积;实际上这里解的范围都是属于[0 ,自己取模数) )

    这点十分重要呢,它不仅证明了解的求法。并且假设有随意一个方程无解,那么整个就都是无解的。

    攻克了这一点之后,就是第二点:怎样处理一个方程的解的个数。

    暂且令当前方程为x^A =B (mod p^d);

    由于中国剩余定理要求取模数互质。所以将2*K+1分解质因子作为全部方程;

    然而我仅仅会处理p^1的情况怎么办啊= =;

    分类讨论:

    p^d|B :这时p^d是B的因子,那么实际上就是x^A =0 (mod p^d)了;

    假设设x=p^k*q。这里(p,q)=1;

    k事实上表示的是x中p因子的个数,那么在x^A中的个数就是k*A,也就有k*A>=d;

    k>=d/A,也就是说k最小为ceil(d/A)  (ceil为上取整函数);

    由于x的取值范围是[0,p^d),而x=p^k*q ,显然d>k。

    所以解的个数就是p^d/p^k=p^(d-k)。


    gcd(p^d,B)=p^k:这时二者之间有一个公约数;

    直观的想。假设等式两边同一时候除一个p^k就解决啦;

    可是要保证A|k,要不然的话x^A中约数p的个数就不可能和B的相等(显然);

    当我们成功的把方程p^k*x'^A= B (mod p^d)

    化简为 x'^A= B/(p^k) (mod p^(d-k))之后

    我们就挂啦!

    由于这样是不正确的,确切的说是x'的取值范围变化了;

    那么变化了多少呢?原来的范围是[0,p^(d-k/A))。而后变成了[0,p^(d-k))。

    原因就在于mod值的缩小,可是对于答案的影响和上面一样:

    答案是第二个方程答案的p^(k-k/A)倍。


    第二个方程怎么解? 取指标啊!

    取完指标就是线性同余方程了。方程个数就是gcd(A,φ(p^d));

    可是有个坑,同余方程可能无解啊。

    所以还要大步~小步~BSGS求出一个 以p^d的原根为底,关于B的。对p^d取模的 指标lnB (好TM绕);

    然后推断gcd(A,φ(p^d))| lnB,假设不是因子则无解!

    那么这题就结束了,可喜可贺;


    什么gcd(B,p^d)=1没讨论?事实上不就是上一段怎么解的事吗。

    什么复杂度?我但是BZ这题Rank1 (倒数)的人啊;

    【反正我咋看咋O(n);

    反正我是线性筛搞了个素数表= =

    然并卵。似乎复杂度没有本质改变?


    代码:


    #include<math.h>
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #define N 140142
    using namespace std;
    typedef long long ll;
    const ll INF=0x7f7f7f7f7f7f7f7f7fll;
    struct Hash_Set
    {
    	ll head[N],next[N],X[N],val[N],tot;
    	void clear()
    	{
    		tot=0;
    		memset(head,0,sizeof(head));
    		memset(next,0,sizeof(next));
    		memset(val,-1,sizeof(val));
    		memset(X,0,sizeof(X));
    	}
    	ll& operator [](ll x)
    	{
    		ll index=x%N;
    		for(ll i=head[index];i;i=next[i])
    		{
    			if(X[i]==x)
    				return val[i];
    		}
    		next[++tot]=head[index];
    		head[index]=tot;
    		X[tot]=x;
    		return val[tot];
    	}
    }hash;
    ll pri[N],top,a[N],b[N];
    bool vis[N];
    void init()
    {
    	for(ll i=2;i<N;i++)
    	{
    		if(!vis[i])
    			pri[++top]=i;
    		for(ll j=1;j<=top&&i*pri[j]<N;j++)
    		{
    			vis[i*pri[j]]=1;
    			if(i%pri[j]==0)
    				break;
    		}
    	}
    }
    ll pow(ll x,ll y,ll mod)
    {
    	ll ret=1;
    	while(y)
    	{
    		if(y&1)
    			ret=ret*x%mod;
    		x=x*x%mod;
    		y>>=1;
    	}
    	return ret;
    }
    ll gcd(ll a,ll b)
    {
    	ll t=a%b;
    	while(t)
    	{
    		a=b,b=t;
    		t=a%b;
    	}
    	return b;
    }
    void exgcd(ll a,ll b,ll &x,ll &y,ll &d)
    {
    	if(!b)
    		x=1,y=0,d=a;
    	else
    	{
    		exgcd(b,a%b,y,x,d);
    		y-=a/b*x;
    	}
    }
    ll inv(ll X,ll mod)
    {
    	ll x,y,d;
    	exgcd(X,mod,x,y,d);
    	return (x%mod+mod)%mod;
    }
    ll Root(ll x,ll phi)
    {
    	ll st[N],top=0,temp=phi;
    	for(ll i=1,p=2;p*p<=temp;i++,p=pri[i])
    	{
    		if(temp%p==0)
    		{
    			st[++top]=phi/p;
    			while(temp%p==0)
    				temp/=p;
    		}
    	}
    	if(temp!=1)
    		st[++top]=phi/temp;
    	for(ll i=1,j;i<=phi;i++)
    	{
    		for(j=1;j<=top;j++)
    		{
    			if(pow(i,st[j],x)==1)
    				break;
    		}
    		if(j>top)
    			return i;
    	}
    }
    ll BSGS(ll A,ll B,ll C)
    {
    	hash.clear();
    	ll bk=ceil(sqrt(C*1.0)),i,k,D,temp;
    	for(i=0,D=1;i<bk;i++,D=D*A%C)
    	{
    		if(hash[D]==-1)
    			hash[D]=i;
    	}
    	temp=inv(D,C);
    	for(i=0,k=B;i<=bk;i++,k=k*temp%C)
    	{
    		if(hash[k]!=-1)
    			return i*bk+hash[k];
    	}
    	return 0;
    }
    ll slove(ll A,ll B,ll C)
    {
    	ll i,j,k,p,mod,lnB,g,d,cnt,ans;
    	for(i=1,p=2,ans=1;p*p<=C;i++,p=pri[i])
    	{
    		if(C%p==0)
    		{
    			mod=1,cnt=0;
    			while(C%p==0)
    				C/=p,mod*=p,cnt++;
    			if(B%mod==0)
    			{
    				d=pow(p,cnt-ceil(cnt*1.0/A),INF);
    			}
    			else
    			{
    				k=0;
    				while(B%p==0)
    					k++,B/=p;
    				if(k%A)	d=0;
    				else
    				{
    					g=Root(mod,mod-mod/p);
    					lnB=BSGS(g,B,mod);
    					d=gcd(A,mod-mod/p);
    					if(lnB%d)	d=0;
    					d*=pow(p,k-k/A,INF);
    				}
    			}
    			ans*=d;
    		}
    	}
    	if(C!=1)
    	{
    			mod=C,p=C,cnt=1;
    			if(B%mod==0)
    			{
    				d=pow(p,cnt-ceil(cnt*1.0/A),INF);
    			}
    			else
    			{
    				k=0;
    				while(B%p==0)
    					k++,B/=p;
    				if(k%A)	d=0;
    				else
    				{
    					g=Root(mod,mod-mod/p);
    					lnB=BSGS(g,B,mod);
    					d=gcd(A,mod-mod/p);
    					if(lnB%d)	d=0;
    					d*=pow(p,k-k/A,INF);
    				}
    			}
    			ans*=d;
    	}
    	return ans;
    }
    int main()
    {
    	init();
    	ll c,T,A,B,C;
    	scanf("%lld",&T);
    	for(c=1;c<=T;c++)
    	{
    		scanf("%lld%lld%lld",&A,&B,&C);
    		printf("%lld
    ",slove(A,B,C<<1|1));
    	}
    	return 0;
    }
    



  • 相关阅读:
    34.初识搜索引擎及timeout机制
    33.bulk json格式的理解
    32.es读请示内部分发原理
    31.分片和复制带来的副本一致性
    30.es增删改内部分发原理
    29.es路由原理
    27.初识分布式文档存储系统慨念
    26.bulk批量操作
    26.mget批量查询
    25.partial update内置乐观锁并发控制
  • 原文地址:https://www.cnblogs.com/clnchanpin/p/7137365.html
Copyright © 2011-2022 走看看