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]计算器(快速幂+扩欧+bsgs)
[bzoj2242][Sdoi2011]计算器_exgcd_BSGS