@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 的情况。注意把负数转成正数。

posted @ 2020-03-09 10:13  Tiw_Air_OAO  阅读(217)  评论(0编辑  收藏  举报