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);
}

posted @ 2021-08-12 19:58  zJx-Lm  阅读(48)  评论(0编辑  收藏  举报