noip模擬【array】
阿新 • • 發佈:2018-10-27
最小 play none char int wap space unsigned 100%
array
by ysy
【題目描述】
給定一個長度為n的數列,每次你可以進行以下操作之一:
(1)將一個數+a;
(2)將一個數-a;
(3)將一個數+b;
(4)將一個數-b;
你需要將所有數全部變為0,求最小操作數。
【輸入數據】
第一行三個整數n,a,b,第二行n個整數x1~xn表示數列。
【輸出數據】
一行一個整數表示答案。無解輸出-1。
【樣例輸入】
2 2 3
1 2
【樣例輸出】
3
【數據範圍】
對於10%的數據,n,a,b,|xi|<=1000。
對於30%的數據,n,a,b<=1000。
對於另外10%的數據,a=1。
對於另外10%的數據,a=2,b=3。
對於100%的數據,1<=n<=105,1<=a,b<=109,|xi|<=109。
【題解思路】
很容易轉化成數學模型:ax+by = c,使(|x|+|y|)min。
對於方程ax+by = c,我們可以用exgcd求出一組解。
當a,b互質時,保證ax+by = c有解。
設d = gcd(a,b).a/d*x+b/d*y = c/d;
此時可求出一組特解:x‘,y‘。
則ax+by = c的通解可以表示為:x = c/d * x‘ + k * b/d,y = c/d * y‘ - k * a/d;
然後如何使(|x|+|y|)min。考慮到對於上述通解,我們可以打表或意念理解,這是個單峰函數。
即存在唯一且確定值k,使得|c/d*x‘ + k*b/d|+|c/d*y‘ - k* a/d|最小,盡可能使絕對值接近零,那麽對於這兩個數使得x取得最小的正數或最大的負數(絕對值盡量接近0)。
時間復雜度 O(nlog|xi|)
#include<bits/stdc++.h> using namespace std; #define ll long long #define ull unsigned long long #define rep(k,i,j) for(int k = i;k <= j; ++k) #define FOR(k,i,j) for(int k = i;k >= j; --k) inlineView Codeint read(){ int x=0,f=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();} return x*f; } int n,a,b,k; inline void exgcd(int a,int b,int m,ll &x,ll &y){ if(!b) x = m/a,y = 0; else { exgcd(b,a%b,m,x,y); swap(x,y); y -= a/b*x; } } inline int gcd(int a,int b){return b ? gcd(b,a%b) : a;} ll x,y,p; int main(){ freopen("array.in","r",stdin); freopen("array.out","w",stdout); n = read(),a = read(),b = read(); k = gcd(a,b); a /= k,b /= k; if(a<b) swap(a,b); rep(i,1,n){ int j = read(); if(j%k) printf("-1\n"),exit(0); exgcd(a,b,j/k,x,y); if(y<0) { x -= b*((-y)/a+1); y += a*((-y)/a+1); } x += b*(y/a); y -= a*(y/a); p += min(abs(x)+abs(y),abs(x+b)+abs(y-a)); } printf("%lld\n",p); return 0; } /* 2 2 3 1 2 */
noip模擬【array】