标签:个数 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;
}
标签:个数 read pre long temp 运用 转换 假设 std
原文地址:https://www.cnblogs.com/Garen-Wang/p/9696017.html