扩展欧几里得求多组解
扩展欧几里得可以求得满足ax+by = c的一组解(这里c%gcd(a,b)要等于0);假如求得的解为(x0 , y0),G = gcd(a , b),这时我们可以发现(x0 + k(b / G) , y0 - k(a / G))就是方程的通解,我们可以通过二分、取模求最小解等等
CodeForces - 1244C
题意:给出两个方程
x + y + z = n
x * w + y * d = p
求解任一满足条件的x,y。
思路:我们可以看出z与方程无关,只用考虑x + y <= n即可;于是我们使用exgcd求出某一组解(x0 , y0),但是这个解可以含负数或加起来大于n,于是我们二分枚举通解中的k,直到遇到可行解。本题在二分过程中会爆long long,所以在中间使用了__int128,最后还是借助了测试数据过掉了这个题,网上也有多种解法值得思考
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define IO ios::sync_with_stdio(false)
#define bug cout << "-----\n"
typedef long long ll;
typedef __int128 INT;
const int N = 100010;
int Mod = 1000000007;
void exgcd(INT a,INT b,INT &x,INT &y) {
if(b == 0) {
x = 1;y = 0;
return ;
}
exgcd(b , a % b , x , y);
INT x1 = x,y1 = y;
x = y1;y = x1 - a / b * y1;
return ;
}
int check(INT t,INT x,INT y,INT w,INT d,INT G,INT n) {
x = x + t * (d / G);y = y - t * (w / G);
if(x + y <= n && x >= 0 && y >= 0)return 0;
if(x < 0) {
return 1;
}
if(y < 0) {
return -1;
}//这里的两个if是借助了数据~~~
if(x + y > n)return 1;
else if(x + y <= n)return -1;
}
int main() {
ll n,p,w,d;cin >> n >> p >> w >> d;
INT n1 = n,p1 = p,w1 = w,d1 = d;
INT G = __gcd(w1 , d1);
if(p1 % G) {
cout << -1;
return 0;
}
INT x,y;
exgcd(w1 , d1 , x , y);
x *= (p1 / G);y *= (p1 / G);
INT l = -1e20,r = 1e20;//l要从-1e20开始,不能从0开始
INT mid;
INT flag = 0;
ll x1 = x,y1 = y;
while(l < r){
mid = l + r >> 1;
int t = check(mid , x , y , w , d , G , n);
if(t == 0) {
flag = 1;
break;
}
else if(t == -1)r = mid;
else l = mid + 1;
}
if(!flag) {
cout << -1 << endl;
}
else {
x1 = x1 + (ll)mid * (d1 / G);
y1 = y1 - (ll)mid * (w1 / G);
cout << x1 << ' ' << y1 << ' ' << n - x1 - y1 << endl;
}
}