@51nod - 1479@ 小Y的数论题
@description@
小Y喜欢研究数论,并且喜欢提一些奇怪的问题。
这天他找了三个两两互质的数a, b, c,以及另一个数m, 现在他希望找到三个(0, m)范围内的整数x, y, z,使得:
\[x^a + y^b \mod m = z^c \mod m
\]
@solution@
这是什么神仙构造题。。。
先不考虑模数。尝试构造一个满足 \(x^a + y^b = z^c\) 的解。
注意到平凡情况下总有 \(2^{n - 1} + 2^{n - 1} = 2^n\),于是我们尝试往这边凑:
\[\begin{cases}
x = 2^p, y = 2^q, z = 2^r\\
ap = bq = n - 1\\
cr = n\\
\end{cases}
\]
如果再记 \(p = bt, q = at\),则可以得到不定方程 \(cr = abt + 1\)。用 exgcd 解出该方程即可。
不过,当模数为 2^k 时,该方法构造出来的数有可能模意义下为 0。我们尝试特判。
注意到此时有 \((2^{k-1})^a = 0 \mod 2^k (a > 1)\)。可以分类讨论 a, b, c 哪些为 1,然后特殊构造方案。
@accepted code@
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y) {
if( b == 0 ) {
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= a/b*x; return d;
}
int m;
int pow_mod(int b, ll p) {
int ret = 1;
for(ll i=p;i;i>>=1,b=1LL*b*b%m)
if( i & 1 ) ret = 1LL*ret*b%m;
return ret;
}
void solve() {
int a, b, c; scanf("%d%d%d%d", &m, &a, &b, &c);
for(int i=2;i<=30;i++)
if( (1 << i) == m ) {
int mid = m / 2;
if( c == 1 )
printf("%d %d %d\n", 1, 1, 2);
else if( b != 1 )
printf("%d %d %d\n", 1, mid, 1);
else if( a != 1 )
printf("%d %d %d\n", mid, 1, 1);
else printf("%d %d %d\n", 2, m - 1, 1);
return ;
}
ll x, y, d = exgcd(c, -1LL*a*b, x, y);
if( d < 0 ) x *= -1, y *= -1, d *= -1;
if( x < 0 ) {
ll p = (-x+1LL*a*b-1)/(1LL*a*b);
x += p*a*b, y += p*c;
}
if( y < 0 ) {
ll p = (-y+c-1)/c;
x += p*a*b, y += p*c;
}
printf("%d %d %d\n", pow_mod(2, b*y), pow_mod(2, a*y), pow_mod(2, x));
}
int main() {
int T; scanf("%d", &T);
while( T-- ) solve();
}
@details@
因为系数有负,所以 exgcd 可能解出来的是 ax + by = -1 的情况。注意把负数转成正数。