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