P2485 [SDOI2011]计算器

Posted garen-wang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2485 [SDOI2011]计算器相关的知识,希望对你有一定的参考价值。

细节决定成败!!!

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

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

第二个操作: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 imes y‘,y=y0+k imes 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!
")
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
", 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
", 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
", ((x % temp) + temp) % temp);
            }
        }
        else if(K == 3)
        {
            ll ans = bsgs(y, z, p);
            if(ans == -1) orz;
            else printf("%lld
", ans);
        }
    }
    return 0;
}

以上是关于P2485 [SDOI2011]计算器的主要内容,如果未能解决你的问题,请参考以下文章

P2485 [SDOI2011]计算器

P2485 [SDOI2011]计算器(快速幂+扩欧+bsgs)

[SDOI2011]计算器

[bzoj2242][Sdoi2011]计算器_exgcd_BSGS

BZOJ_2242_[SDOI2011]计算器_快速幂+扩展GCD+BSGS

bzoj2242[SDOI2011]计算器