hdu 5377 Root 原根+离散对数+扩展欧几里得

来源:互联网 发布:mac文件归类 编辑:程序博客网 时间:2024/06/05 07:04

题意:给定一个数字sum,有 m 个询问:(xi, yi),求最小的非负整数 ki 满足 xi^ki =yi (mod p)。

            其中 p 是 sum 的质因子。

            1<=sum<=10^8,1<=m<=10^5,0<=xi, yi<=10^9

分析:

直接用离散对数求解K的复杂度为O( m log m ),m为根号n。对于该题每次询问都要单独计算,复杂度太高。

需要用到原根,离散对数,扩展欧几里得计算。

整体思路:

对sum分解质因数, 对于x^k mod p = y ,

先求出p的原根d,对同余式两边取对数得到  k*logd(x) mod p-1 = logd(y)

再使用扩展欧几里德求出上式中的k

复杂度优化!!:

令:A=logd(x)  B=logd(y)

对同余式取对数时:即 求d^A mod p = x      d^B mod p = y

baby step中要对d^m打表,由于对于同一质因子p,d是相同的。所以可以提前初始化表。

那么总复杂度变为O(m+p/m*q),p为质数大小,m为表长,q为查询次数

其中查询次数 q 为10^5,所以表要足够大,以使p/m*q这一项较小。


#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>#include <string>#include <cmath>#include <vector>#include <map>#include <tr1/unordered_map>#define clr(x, y) memset(x, y, sizeof x)using namespace std;typedef long long LL;const double eps=1e-8;const int maxn=100100;const int mod=1e6+10;LL prime[maxn];LL X[maxn],Y[maxn];LL A[maxn],B[maxn];LL ans[maxn];LL M;//质因数分解LL get_prime(LL n){    LL cnt=0;    for(LL i=2;i*i<=n;i++)    {        if(n%i==0)        {            prime[cnt++]=i;            while(n%i==0)                n/=i;        }    }    if(n!=1)        prime[cnt++]=n;    return cnt;}//快速幂LL pow_mod(LL a,LL p,LL n){    LL ans=1;    while(p)    {        if(p&1)            ans=(ans*a)%n;        p>>=1;        a=(a*a)%n;    }    return ans;}//求原根vector<LL>a;bool g_test(LL g,LL p){    for(LL i=0;i<a.size();i++)    {        if(pow_mod(g,(p-1)/a[i],p)==1)            return 0;    }    return 1;}LL primitive_root(LL p){    LL tmp=p-1;    a.clear();    for(LL i=2;i<=tmp/i;i++)    {        if(tmp%i==0)        {            a.push_back(i);            while(tmp%i==0)                tmp/=i;        }    }    if(tmp!=1)        a.push_back(tmp);    LL g=1;    while(true)    {        if(g_test(g,p))            return g;        g++;    }}//扩展欧几里得void gcd(LL a,LL b,LL &d,LL &x,LL &y){    if(!b) {  d=a;  x=1;  y=0;}    else    {        gcd(b,a%b,d,y,x);        y-=x*(a/b);    }}//求逆元LL inv(LL a,LL n){    LL d,x,y;    gcd(a,n,d,x,y);    return d==1 ? (x+n)%n : -1;}//手动哈希struct HashTable{    int top,head[mod];    struct Node    {        int x,y,next;    }node[mod];    void init()    {        top=0;        memset(head,0,sizeof(head));    }    void insert(LL x,LL y)    {        node[top].x=x;        node[top].y=y;        node[top].next=head[x%mod];        head[x%mod]=top++;    }    LL query(LL x)    {        for(int tx=head[x%mod];tx;tx=node[tx].next)        {            if(node[tx].x==x)                return node[tx].y;        }        return -1;    }}hs;//求出所有查询中X,Y关于a(mod n)的离散对数void log_mod(LL a,LL n){    LL m,v,e=1;    m=mod;    v=inv(pow_mod(a,m,n),n);    //打表 a^0 ~ a^m-1, m为表长    hs.init();    for(int i=0;i<m;i++)    {        hs.insert(e,i);        e=e*a%n;    }    //求出所有查询中X,Y关于a的离散对数    for(int j=0;j<M;j++)    {        //必须要取模后再计算        LL b=X[j]%n;        for(int i=0;i<m;i++)        {            LL s=hs.query(b);            if(s!=-1)            {                A[j]=i*m+s;                break;            }            b=b*v%n;        }        b=Y[j]%n;        for(int i=0;i<=n/m;i++)        {            LL s=hs.query(b);            if(s!=-1)            {                B[j]=i*m+s;                break;            }            b=b*v%n;        }    }}//用扩展欧几里得求a*x+b*y=c中x的最小非负整数解LL get_K(LL a,LL b,LL c){    LL d,x,y;    gcd(a,b,d,x,y);    if(c%d!=0)        return -1;    x=x*c/d;    b=b/d;    x=x-(x/b*b);    if(x<0)        x+=b;    return x;}int main(){    int T;    LL sum;    scanf("%d",&T);    for(int t=1;t<=T;t++)    {        memset(ans,-1,sizeof(ans));        scanf("%I64d %I64d",&sum,&M);        printf("Case #%d:\n",t);        for(int i=0;i<M;i++)        {            scanf("%I64d %I64d",&X[i],&Y[i]);        }        //质因数分解        LL cnt=get_prime(sum);        LL temp;        //枚举所有质因子        for(int j=0;j<cnt;j++)        {            //求该质因子的原根d            LL d=primitive_root(prime[j]);            //求所有的查询中X,Y关于d(mod prime[j])的离散对数            log_mod(d,prime[j]);            for(int i=0;i<M;i++)            {                //求解最小的K, K*A[i]=B[i](mod (prime[j]-1))                temp=get_K(A[i],prime[j]-1,B[i]);                if(temp!=-1)                {                    if(ans[i]==-1)                        ans[i]=temp;                    else                        ans[i]=min(temp,ans[i]);                }            }        }        for(int i=0;i<M;i++)            printf("%I64d\n",ans[i]);    }    return 0;}


0 0
原创粉丝点击