Mod Tree(hdu2815高次线性同余方程)

题意:求每个节点有K个儿子,计算最小深度D的节点数对P取模的结果是N,K^D = N( mod P )的最小D值 如果无解输出 Orz,I can’t find D!

思路:baby__giant算法

分两种情况

第一种 : 当A 和C互质的时候

m = sqrt(C);

A^x = A^i*m+j = B (mod C)

A^i*m*A^j = B ( mod C)

两边除以A^i*m,因为A 和C互质 所以除以A^i*m等于乘以A^i*m的逆元

什么是逆元,当A和C互质 A*b = 1 (mod C) b就是A的乘法逆元 ,所以当存在逆元 B/A = 2 (mod C) 就可以写成B*b = 2(mod C)

所以用扩展欧几里德 求出A^i*m的逆元等于A^-i*m, 化简等于

A^j = A^-i*m * B ( mod C) (0 <= j <= m)

我们可以先算出所有A^j的值存在hash中然后遍历i,计算A^i*m的逆元再乘以B mod C看它的值是否在hash中出现过,如果出现过返回i*m+j

第二种 :当A和C不互质的时候,因为不互质就没有逆元,所以我们要想办法让他们互质 计算A和C的最大公约数

因为 A^x = B (mod C) 如果d = gcd(A,C)  那么 A/d*A^x-1 = B/d ( mod C/d ) 这样一直除下去一直到A和C互质即 (A/d)^c * A^x-c = B/d^c ( mod C / d^c )

这样 A^x-c与C/d^c互质就可以求逆元了,

令k = x-c 紧接着套用上面的计算方式 将k写成  k = i*m+j

然后 (A/d)^c * A^i*m * A^j= B/d^c ( mod C / d^c ) 两边同时除以(A/d)^c * A^i*m,也就是用扩展欧几里德求他们的逆元等于

A^j= B/d^c* ((A/d)^c * A^i*m)^-1 *( mod C / d^c ) 这样还是先算出所有A^j存在hash中然后遍历i,计算((A/d)^c * A^i*m)^-1的逆元再乘以 B/d^c看他的值是否在hash出现过如果出现过返回 i*m+j 因为k = x - c 所以最后的k 还要+ c等于x =  i*m+j+c

如果在i的范围(0<= i <= m)都没找到合适的值或者再在化简为互质那一步B/d不能整除那就是无解

因为当解比较小的时候我们可以暴力,那么我们先计算0~~100的如果有就直接返回没有在用上述的算法


本题注意N在 (0 < = N < P) 大于P的都是无解的,还有就是当N = 0的时候就是D = 0,还有大坑就是那个无解的英文千万不能copy

A^x = B (mod p)  0 <= B < p 注意范围,看题目要求如果没有给就按照超范围无解

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
typedef long long int64;

struct Point
{
    int64 x;
    int64 y;
    Point (int64 x,int64 y) : x(x),y(y){}
};
vector<Point> v[10005];
int MOD = 10000;

void insert(int64 x,int64 j)
{
    int64 k = x % MOD;
    Point a(x,j);
    v[k].push_back(a);
}
int64 Find(int64 x)
{
    int64 k = x % MOD;
    int64 temp = v[k].size();
    for(int64 i = 0; i < temp; i++)
    {
       // printf("%I64d\n",x);
        if(v[k][i].x == x)
            return v[k][i].y;
    }
    return -1;
}

int64 gcd(int64 a,int64 b)
{
    if(b == 0)
        return a;
    else
        return gcd(b,a%b);
}

int64 Exgcd(int64 a,int64 b,int64&x,int64 &y)
{
    if(b == 0)
    {
        x = 1,y = 0;
        return a;
    }
    else
    {
        int64 r = Exgcd(b,a%b,x,y);
        int64 temp = x;
        x = y,y = temp - a/b*y;
        return r;
    }
}

int64 Inval(int64 a,int64 b,int64 n)
{
    int64 x,y,d;
    d = Exgcd(a,b,x,y);//printf("%I64d %I64d\n",x,y);
    x = x *n % b;
    x = ( x % b + b) % b;
    return x;
}

int64 baby_gaint(int64 B,int64 N,int64 P)
{
    int64 sum = 1%P;
    for(int64 i = 0; i <= 100; i++)
    {
        if(sum % P == N)
            return i;
        sum = ( sum * B ) % P;
    }
    int64 cnt = 0,D = 1%P,j,tmp;
   while((tmp = gcd(B,P)) != 1)
    {
        if(N % tmp)
        {
            return -1;
        }
        cnt++;
        N = N / tmp;
        P = P / tmp;
        D = D * B / tmp % P;
    }

    int64 n = (sqrt(1.0*P));
    sum = 1;
    for(int64 i = 0; i < n; i++,sum = (sum * B) % P)
    {
        insert(sum,i);
    }
    int64 X;
    for(int64 i = 0;; D = D * sum % P,i+= n)
    {
        X = Inval(D,P,N);//printf("%I64d %I64d %I64d\n",X,P,N);
        if(X >= 0 && ( j = Find(X) ) != -1)
        {
            return i + j + cnt;
        }
        if(i > P) break;
    }
    return -1;
}
int main()
{
    int64 K,N,P;
    while(scanf("%I64d%I64d%I64d",&K,&P,&N) != EOF)
    {
        for(int i = 0; i < 10005; i++)
            v[i].clear();
        int64 temp = baby_gaint(K,N,P);
        if(N >= P)
        {
            printf("Orz,I can’t find D!\n");continue;
        }
        if(N == 0)
        {
            printf("0\n");continue;
        }
        if(temp == -1)
            printf("Orz,I can’t find D!\n");
        else
            printf("%I64d\n",temp);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值