码迷,mamicode.com
首页 > 其他好文 > 详细

P2485 [SDOI2011]计算器

时间:2018-09-24 18:33:02      阅读:163      评论:0      收藏:0      [点我收藏+]

标签:个数   read   pre   long   temp   运用   转换   假设   std   

细节决定成败!!!

这道题说上去是计算器,其实就是考你三个数论知识。

第一个操作:卡速米模板。。。

第二个操作:exgcd的运用。我并不会,这里记录一下。

对于一个\(xy \equiv z \pmod p\),我们为了方便,换成\(ax \equiv z \pmod p\)

根据同余的性质,得:\(ax - py = z\),这里我们设了个\(y\)

这种一般的二元不定方程并不知道怎么求,但是\(z\)变成\(gcd(a,p)\)我们就会求了

所以我们可以解另一个方程:\(ax_0-py_0=gcd(a,p)\)

如何求出\(x\)

这两条等式是可以相互转换的。也就是满足\(x:x_0=z:gcd(a,p)\),得\(x=\frac{zx_0}{gcd(a,p)}\)

但是这个东西不一定是正的。我们在通解中找出一个最小的正数解。

这里有一个重要结论,没了这个结论没法做:

对于一般的二元不定方程\(ax+by=c\),假设你已经求出了一对解\((x_0, y_0)\),则\(x=x_0+k \times y‘,y=y0+k \times x‘\),其中\(x‘=\frac{x}{gcd(a,p)},y‘=\frac{y}{gcd(a,p)}\)

为什么逆元里面通解的倍数是\(p\)?因为人家gcd等于1。。。

所以把求逆元那样,把\(p\)换成\(y‘\)就是了。

第三个东西是离散对数,用BSGS算法。万幸没有拓展。

有一个很坑的地方:你需要先对\(z\)取模,然后再特判,而不能先特判后取模。不然你25分就没了。。。

代码:

#include<cstdio>
#include<cmath>
#include<map>
#define ll long long
#define orz printf("Orz, I cannot find x!\n")
ll read()
{
    ll ans = 0, s = 1;
    char ch = getchar();
    while(ch > ‘9‘ || ch < ‘0‘){ if(ch == ‘-‘) s = -1; ch = getchar(); }
    while(ch >= ‘0‘ && ch <= ‘9‘) ans = (ans << 3) + (ans << 1) + ch - ‘0‘, ch = getchar();
    return s * ans;
}
ll pow_mod(ll y, ll z, ll p)
{
    ll ans = 1, base = y;
    while(z)
    {
        if(z & 1) ans = ans * base % p;
        base = base * base % p;
        z >>= 1;
    }
    return ans % p;
}
ll gcd(ll x, ll y)
{
    return y == 0 ? x : gcd(y, x % y);
}
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b == 0){ x = 1; y = 0; return a; }
    ll ret = exgcd(b, a % b, x, y);
    ll t = x;
    x = y;
    y = t - a / b * y;
    return ret;
}
ll bsgs(ll y, ll z, ll p)
{
    // y^x === z (mod p)
    // set m = sqrt(p) + 1, x = a * m - b
    // y^(a * m - b) === z (mod p)
    // y^(a * m) === z * y^b (mod p)
    // b \in [0, m)   a \in (0, m + 1]
    
    z %= p;
    if(y == 0)
    {
        if(z == 0) return 1;
        else return -1;
    }
    std::map<ll, ll> mp;
    ll m = sqrt(p) + 1;
    for(int i = 0; i < m; i++)
    {
        ll val = z * pow_mod(y, i, p) % p;
        if(!mp.count(val)) mp[val] = i;
    }
    ll giant = pow_mod(y, m, p);
    if(giant == 0)
    {
        if(z == 0) return 1;
        else return -1;
    }
    for(int i = 0; i <= m; i++)
    {
        ll val = pow_mod(giant, i, p);
        int j = mp.find(val) == mp.end() ? -1 : mp[val];
        if(j >= 0 && i * m - j >= 0) return i * m - j;
    }
    return -1;
}
int main()
{
    /*
    ll y = read(), z = read(), p = read();
    printf("%lld\n", pow_mod(y, z, p));
    return 0;
    */
    //freopen("in.txt", "r", stdin);
    int T = read(), K = read();
    while(T--)
    {
        ll y = read(), z = read(), p = read();
        if(K == 1) printf("%lld\n", pow_mod(y, z, p));
        else if(K == 2)
        {
            // x * y === z (mod p)
            // let a = y
            // a * x === z (mod p)
            // a * x - p * y = z
            // a * xx - p * yy = gcd(a, p)
            ll xx, yy;
            ll g = exgcd(y, p, xx, yy);
            if(z % g != 0) orz;
            else
            {
                ll temp = p / g;
                // x : xx = z : g
                ll x = xx * z / g;
                printf("%lld\n", ((x % temp) + temp) % temp);
            }
        }
        else if(K == 3)
        {
            ll ans = bsgs(y, z, p);
            if(ans == -1) orz;
            else printf("%lld\n", ans);
        }
    }
    return 0;
}

P2485 [SDOI2011]计算器

标签:个数   read   pre   long   temp   运用   转换   假设   std   

原文地址:https://www.cnblogs.com/Garen-Wang/p/9696017.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!