1. 程式人生 > 其它 >noip模擬37 數列

noip模擬37 數列

一道擴歐板子。實際上就讓我們解 \(n\)\(ax+by=c\) 形式的方程,方程有解當且僅當 \((a,b)|c\)
再回憶一下擴歐吧
對於一個擴歐方程 ,我們解的答案僅是滿足 \(ax+by=(a,b)\) 形式的,要想轉變為上述形式,等式左右同除 \((a,b)\) 再乘 \(c\) 即可,於是變成 \(a\frac{cx}{(a,b)}+b\frac{cy}{(a,b)}=c\),題意讓我們找 \(abs(x')+abs(y')\) 的最小值,考慮答案的集和。

其實很簡單,我們發現 \(a(\frac{cx}{(a,b)}\pm k*\frac{b}{(a,b)})+b(\frac{cy}{(a,b)}\mp k*\frac{a}{(a,b)})=c\)

,等式依然成立,
考慮 \(\frac{a}{(a,b)}\)\(\frac{b}{(a,b)}\)的大小,我們讓最大的儘量靠近 0 就行了,注意記得在 0 旁徘徊一下,以提高答案的準確率。

code:

#include <bits/stdc++.h>
#define re register
#define int long long
using namespace std; 
const int maxn=100010;
const int INF=1e18;
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 s=0,w=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') w=-1;ch=getchar(); }
    while(ch>='0'&&ch<='9') { s=s*10+ch-'0'; ch=getchar(); }
    return s*w; 
} 

int x,y;
inline int exgcd(int a,int b) {
	if(!b) { x=1,y=0; return a; }
	int gcd=exgcd(b,a%b),tmp=x;
	x=y;
	y=tmp-a/b*y;
	return gcd;
}
int n,a,b,c[maxn];
signed main(void) {
	//freopen("array.in","r",stdin);
	//freopen("cs.txt","w",stdout);
	n=read(),a=read(),b=read();
	int gcd=exgcd(a,b); //cout<<x<<' '<<y<<endl;
	for(re int i=1;i<=n;i++) {
		c[i]=read();
		if((c[i]%gcd)) { printf("-1\n"); return 0; }
	}
	a/=gcd; b/=gcd;
	int res=0,date; date=(a>b)? 1:0;
	for(re int i=1,ans,x1,y1;i<=n;i++) {
		x1=x*c[i]/gcd,y1=y*c[i]/gcd; ans=INF;
		if(date) {
			int t=y1/a; y1-=t*a; x1+=t*b;
			ans=min(ans,abs(x1)+abs(y1));
			if(y1>0) {
				y1-=a,x1+=b;
				ans=min(ans,abs(x1)+abs(y1));
			}
			else {
				y1+=a,x1-=b;
				ans=min(ans,abs(x1)+abs(y1));
			}
		}
		else {
			int t=x1/b; x1-=t*b; y1+=t*a;
			ans=min(ans,abs(x1)+abs(y1));
			if(x1>0) {
				x1-=b,y1+=a;
				ans=min(ans,abs(x1)+abs(y1));
			}
			else {
				x1+=b,y1-=a;
				ans=min(ans,abs(x1)+abs(y1));
			}
		}
		res+=ans;
	}
	printf("%lld ",res);
}