P1516 青蛙的约会(洛谷)
题意:在一个循环坐标轴上,有两个初始坐标为x,y的点,每个人一次跳m,n步,求解是否可以相遇,如果可以求出最小步数。
假设跳了k步后两点相遇。则有
x+k*m - (y+k*n) = T * L(T为常数,即表示为L的常数倍,因为是循环坐标轴)
转化为 TL+(n-m)*k = x-y;
设 a = L,b = n-m,x = t,y = k,c = x-y;
那么式子转化为 ax+by = c;
显然是扩展欧几里得求解。
扩展欧几里得求得 ax0+by0 = gcd(a,b)-+
两边同时乘以c / gcd(a,b) (一定是整除,因为c是gcd(a,b)的倍数)
a*xo*c / gcd(a,b) + b*yo*c / gcd(a,b).
此时
x = xo*c / gcd(a,b);
y = yo*c / gcd(a,b)
x,y即为ax + by = c的一组特解。
关于求解最小正整数解的求法。
x = (x % (b/gcd(a,b)+gcd(a,b) ) % gcd(a,b)
y = (y % (a/gcd(a,b)+gcd(a,b) ) % gcd(a,b)
由定式推得(证明略)
需要注意的是,gcd只对非负整数有效。
b可能是负数,此时需要对等式两边取反,但是a不用取反,因为可以将x看成-T来实现取反效果。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<LL,int> pii; const int N = 1e3+5; const int M = 1e6+5; const LL Mod = 1e9+7; #define rg register #define pi acos(-1) #define INF 1e9 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline LL read() { LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } LL xx,yy,n,m,L; LL exgcd(LL a,LL b,LL &x,LL &y) { if(b == 0) { x = 1,y = 0; return a; } LL t = exgcd(b,a%b,y,x); y -= a/b*x; return t; } int main() { xx = read(),yy = read(),m = read(),n = read(),L = read(); LL a = L,b = n-m,c = xx-yy,x,y; if(b < 0) b = -b,c = -c; LL gcd = exgcd(a,b,x,y); if(c%gcd != 0) printf("Impossible\n"); else { LL ma = c/gcd*y; LL ans = (ma % (a/gcd) + (a/gcd))%(a/gcd); printf("%lld\n",ans); } // system("pause"); return 0; }
The Balance(POJ 2142)
题意:给定两个种类砝码,和一个要称量的物体。计算是否能够通过天平测量出这个物品,如果能,求最少需要的砝码总和。
设a种砝码x个,b种砝码y个。称量的物体种类为c。
即满足a*x + b*y = c.
注意的是砝码可以放左和右。那么这里定义放左为正,右为负,所以x,y可能为负。
这里求的即为abs(x) + abs(y)的最小值。
可以证明,肯定是满足一边为最小正整数个时,总和最小。
那么扩展欧几里得可以计算出x为最小正整数时,和y为最小正整数时的情况,求最小的情况即可。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<LL,int> pii; const int N = 1e5+5; const int M = 1e6+5; const LL Mod = 1e9+7; #define rg register #define pi acos(-1) #define INF 1e9 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline LL read() { LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } int exgcd(int a,int b,int &x,int &y) { if(b == 0) { x = 1,y = 0; return a; } int t = exgcd(b,a%b,y,x); y -= a/b*x; return t; } int main() { int a,b,d,x,y; while((a = read(),b = read(),d = read()),a || b || d) { int gcd = exgcd(a,b,x,y); if(d % gcd != 0) printf("no solution\n"); else { int t1 = x*d/gcd,t2 = y*d/gcd; int ma1 = (t1%(b/gcd)+(b/gcd))%(b/gcd);//最小正整数x int ma2 = (t2%(a/gcd)+(a/gcd))%(a/gcd);//最小正整数y int ma3 = abs((d-a*ma1)/b);//注意取绝对值,因为可能是放在不同边 int ma4 = abs((d-b*ma2)/a);//同理 if(ma1+ma3 < ma2+ma4) printf("%d %d\n",ma1,ma3); else printf("%d %d\n",ma4,ma2); } } system("pause"); return 0; }