hdu 5377 Root 原根+離散對數+擴展歐幾里得

題意:給定一個數字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;
}


發佈了52 篇原創文章 · 獲贊 9 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章