noip模擬37 數列
阿新 • • 發佈:2021-08-12
一道擴歐板子。實際上就讓我們解 \(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); }