先把这个东西差分,那么就从一段区间加减变成了一个点加,另一个点减。
然后把所有数和a,b都除gcd(a,b)
然后每个点可以表示为 d[i]=x[i]∗a+y[i]∗b
用exgcd求ax+by=1的一组解。
那么一个点的次数可以表示为: |x∗d[i]+k∗b|+|y∗d[i]+k∗a|
两个绝对值一次函数套在一起是一个单峰函数,因此可以三分这个k。
不过还需要保证 ∑x∗d[i]+k∗b=0 且 ∑y∗d[i]+k∗a=0
由于 a∗∑x∗d[i]+k∗b+b∗∑y∗d[i]+k∗a=0
因此只需要保证 ∑x∗d[i]+k∗b=0
求出当前的 (∑x∗d[i]+k∗b)/b 就是所有k的总改变量。
然后用优先队列每次找k改变一个单位 |x∗d[i]+k∗b|+|y∗d[i]+k∗a| 变化最少的元素,把它的k改变一个单位。
重复 (∑x∗d[i]+k∗b)/b 次。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 110000
#define PA pair<ll,int>
int n,a,b;
int d[N];
ll pos[N],X[N],Y[N],ans,sum;
void quit(){puts("-1");exit(0);}
void exgcd(ll &x,ll &y,int a,int b)
{
if(b==0){y=0;x=1;return;}
exgcd(y,x,b,a%b);y-=a/b*x;
}
priority_queue<PA,vector<PA>,greater<PA> >q;
ll cal(ll x,ll y,ll v){return abs(x+v*b)+abs(y-v*a);}
int main()
{
scanf("%d%d%d",&n,&a,&b);
int t=__gcd(a,b);a/=t;b/=t;
for(int i=1;i<=n;i++)
{
scanf("%d",&d[i]);
if(d[i]%t)quit();
d[i]/=t;
}
n++;
if(a==b)
{
for(int i=n;i>=1;i--)ans+=abs(d[i]-d[i-1]);
return printf("%lld\n",ans/2),0;
}
ll x,y;exgcd(x,y,a,b);
for(int i=n;i>=1;i--)
{
d[i]=d[i]-d[i-1];
X[i]=x*d[i];Y[i]=y*d[i];
ll l=-abs(d[i]),r=abs(d[i]);
while(l+5<=r)
{
ll lm=l+(r-l)/3,rm=r-(r-l)/3;
if(cal(X[i],Y[i],lm)<cal(X[i],Y[i],rm))r=rm;
else l=lm;
}
for(int j=l;j<=r;j++)
pos[i]=cal(X[i],Y[i],pos[i])<cal(X[i],Y[i],j) ? pos[i]:j;
sum+=X[i]+pos[i]*b;
}
sum/=b;
for(int i=1;i<=n;i++)
{
if(sum<0)q.push(make_pair(cal(X[i],Y[i],pos[i]+1)-cal(X[i],Y[i],pos[i]),i));
else q.push(make_pair(cal(X[i],Y[i],pos[i]-1)-cal(X[i],Y[i],pos[i]),i));
}
while(sum)
{
int t=q.top().second;q.pop();
if(sum<0)
{
pos[t]++;sum++;
q.push(make_pair(cal(X[t],Y[t],pos[t]+1)-cal(X[t],Y[t],pos[t]),t));
}
else
{
pos[t]--;sum--;
q.push(make_pair(cal(X[t],Y[t],pos[t]-1)-cal(X[t],Y[t],pos[t]),t));
}
}
for(int i=1;i<=n;i++)
ans+=cal(X[i],Y[i],pos[i]);
printf("%lld\n",ans/2);
return 0;
}