1. 程式人生 > 其它 >題解 數列 及exgcd總結

題解 數列 及exgcd總結

傳送門

自閉了……考場上exgcd打錯然後對著螢幕自閉了一個小時不知道它為什麼解得不對
開始惡補:
對於方程 \(a*x+b*y=c\) ,就等價於 \(a*x \equiv c\pmod{b}\)
首先它有解的條件是 \(c \mid gcd(a, b)\)
然後exgcd可以用來求一組 \(x, y\) 滿足 \(a*x+b*y=gcd(a, b)\)
所以把解出來的 \(x, y\) 除gcd再乘上c就可以得到一組解
然後\(gcd(a, b)=1\) 的條件下解集為 \((a+k*b, b+k*a)\)
特別注意這裡的 \(gcd(a, b)=1\) ,我被這玩意坑了半天

對應到這道題裡,要求的就是 \(min\{abs(x)+abs(y)\}\)


\(x, y\) 的正負分情況討論,讓 \(abs(x-k*b)+abs(y+k*a)\) 儘量變小
具體來說,當 \(x \geqslant 0, y \geqslant 0\) 時,考慮 \(a, b\) 的大小關係
可以發現要讓大的那個係數為負,比較 \(x\) 是最小的那個正數和是最大的那個負數的大小
其它情況類似

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long 
//#define int long long 

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n; ll a, b;
ll x[N], ans;

ll exgcd(ll a, ll b, ll& x, ll& y) {
	if (!b) {x=1, y=0; return a;}
	ll g=exgcd(b, a%b, y, x);
	y-=(a/b)*x;
	return g;
}

signed main()
{
	n=read(); a=read(); b=read();
	if (a>b) swap(a, b);
	ll x1, y1;
	ll g=exgcd(a, b, x1, y1);
	a/=g, b/=g;
	//cout<<"xy: "<<x1<<' '<<y1<<endl;
	for (int i=1; i<=n; ++i) x[i]=read();
	for (int i=1; i<=n; ++i) {
		//cout<<i<<": "<<endl;
		if (x[i]%g) {puts("-1"); return 0;}
		ll x2=x[i]*x1/g, y2=x[i]*y1/g, k=llabs(x2/b);
		//cout<<"xyk: "<<x2<<' '<<y2<<' '<<k<<endl;
		if (x2>=0 && y2>=0) ans+=min(llabs(x2-k*b)+llabs(y2+k*a), llabs(x2-(k+1)*b)+llabs(y2+(k+1)*a));
		else if (x2<=0 && y2>=0) ans+=min(llabs(x2+k*b)+llabs(y2-k*a), llabs(x2+(k+1)*b)+llabs(y2-(k+1)*a));
		else if (x2>=0 && y2<=0) ans+=min(llabs(x2-k*b)+llabs(y2+k*a), llabs(x2-(k+1)*b)+llabs(y2+(k+1)*a));
		else puts("error");
	}
	printf("%lld\n", ans);
	
	return 0;
}