hdu5377 Root

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5377
题意:
sum,mxi,yi,ki使xkii=yi mod psump
sum<=108,m<=105
分析:离散对数,我们可以采用baby-step算法求解,但是直接做的话单次询问需要 n ,这里就使用了一个技巧:令 x=da mod p , y=db mod p , x,ydp ,于是上式就可以化简为 dak=dbmodp ,由于p是素数,那么显然有 ak=bmodp1 ,这里只需要用扩展欧几里德解一个模方程即可。如何求a,b,仍然需要用baby-step求,但由于每次询问时候的d都是相同的,假设我们预处理的表的大小是sz,那么复杂度就降为 sz+qn/sz ,可以看出sz较大的时候复杂度会比较小

#include<bits/stdc++.h>
using namespace std;
const int Inf=1e9;
typedef long long Int;
bool isp[10020];
vector<int>pri;
int cas=1,mul;
int powmod(int x,int y,int mod)
{
    int ret=1;
    while(y)
    {
        if(y&1)ret=ret*(Int)x%mod;
        x=x*(Int)x%mod;
        y>>=1;
    }
    return ret;
}
void exgcd(int a,int b,int &x,int &y)
{
    b?(exgcd(b,a%b,y,x),y-=a/b*x):(x=1,y=0);
}
void getp()
{
    for(int i=2;i<=10000;i++)
    {
        if(!isp[i])
        {
            pri.push_back(i);
            for(int j=i+i;j<=10000;j+=i)
                isp[j]=1;
        }
    }
}
void divide(int x,vector<int>&V)
{
    for(int i=0;i<pri.size()&&pri[i]*pri[i]<=x;i++)
    {
        if(x%pri[i]==0)
        {
            V.push_back(pri[i]);
            while(x%pri[i]==0)x/=pri[i];
        }
    }
    if(x>1)V.push_back(x);
}
int getd(int x)
{
    if(x==2)return 1;
    vector<int>tp;
    divide(x-1,tp);
    for(int i=2;;i++)
    {
        bool flag=1;
        for(int j=0;j<tp.size();j++)
            if(powmod(i,(x-1)/tp[j],x)==1)
            {
                flag=0;
                break;
            }
        if(flag)return i;
    }
    return -1;
}
int Mp1[100000000];
int Mp2[10][10000];
void get(int &a,int tx,int p)
{
    if(Mp1[tx]>=0)a=Mp1[tx];
    else
    {
        int now=tx;
        for(int i=1;i*1000000<=p;i++)
        {
            now=now*(Int)mul%p;
            if(Mp1[now]>=0){a=Mp1[now]+i*1000000;return;}
        }
    }
}
int getrev(int a,int b)
{
    int x,y;
    exgcd(a,b,x,y);
    return x<0?(x+b):x;
}
int main()
{
    getp();
    int _;scanf("%d",&_);
    while(_--)
    {
        int sum,m;scanf("%d%d",&sum,&m);
        vector<int>allp;
        vector<int>small;
        divide(sum,allp);
        int maxx=-1,maxsz;
        for(int i=0;i<allp.size();i++)
            if(allp[i]<10000)
            {
                small.push_back(allp[i]);
            }
            else
            {
                int now=1,x=getd(allp[i]);
                maxx=allp[i];
                mul=powmod(x,maxx-1-1000000%(maxx-1),maxx);
                maxsz=min(1000000,maxx-1);
                for(int j=1;j<maxx;j++)Mp1[j]=-1;
                for(int j=0;j<maxsz;j++)
                {
                    if(Mp1[now]<0)Mp1[now]=j;
                    now=now*(Int)x%maxx;
                }
            }
        for(int i=0;i<small.size();i++)
        {
            memset(Mp2[i],-1,sizeof(Mp2[i]));
            int now=1,x=getd(small[i]);
            for(int j=0;j<small[i]-1;j++)
            {
                if(Mp2[i][now]<0)Mp2[i][now]=j;
                now=now*(Int)x%small[i];
            }
        }
        printf("Case #%d:\n",cas);
        while(m--)
        {
            int x,y;scanf("%d%d",&x,&y);
            int ans=Inf;
            for(int i=0;i<small.size();i++)
            {
                int p=small[i];
                int tx=x%p,ty=y%p;
                if(tx==0)
                {
                    if(ty==0)ans=min(ans,1);
                    else if(ty==1)ans=min(ans,0);
                    continue;
                }
                else if(ty==0)continue;
                int a=Mp2[i][tx],b=Mp2[i][ty];
                int gc=__gcd(a,p-1);
                if(b%gc)continue;
                int k=b/gc*(Int)getrev(a/gc,(p-1)/gc)%((p-1)/gc);
                ans=min(ans,k);
            }
            if(maxx!=-1)
            {
                int p=maxx;
                int tx=x%p,ty=y%p;
                if(tx==0)
                {
                    if(ty==0)ans=min(ans,1);
                    else if(ty==1)ans=min(ans,0);
                }
                else 
                if(ty)
                {
                    int a,b;
                    get(a,tx,p);
                    get(b,ty,p);
                    int gc=__gcd(a,p-1);
                    if(b%gc==0)
                    {
                        int k=b/gc*(Int)getrev(a/gc,(p-1)/gc)%((p-1)/gc);
                        ans=min(ans,k);
                    }
                }
            }
            if(ans==Inf)ans=-1;
            printf("%d\n",ans);
        }
        cas++;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值