判断有无解,显然就是判断是否 (forall i in [1,n] gcd(a,b) | h_i)。裴蜀定理随便证证就好了。
然后对于一个序列问题,进行区间加减操作,一个非常简单的套路就是做出其差分数组,然后单点修改即可。为了保证最后一个数修改后为 (0),所以我们多加一个数 (h_{n+1}=-h_{n})。
考虑只有两种操作 (a,b)。先想一想一个数的时候我们应该怎么去处理。对于一个数 (s),对其进行操作,设未知数 (x,y) 表示 (a) 操作进行次数和 (b) 操作进行次数(负数就相当于减 (a) 或 (b))相当于解一个等式:
[ax+by=s
]
显然这个问题可以直接用拓展欧几里得算法去做。不赘述。
于是想让 (|x|+|y|) 最小。这个可以随便分类讨论一发,判断一下 (x,y) 分别取极值的时候就行了吧。或者说你也可以根据方程通解(看下面!)三分也行:
[egin{cases}
x=x_0 + k imes dfrac{b}{gcd(a,b)} \
y=y_0 + k imes dfrac{a}{gcd(a,b)}
end{cases}
]
找到这些解之后,我们还要考虑让:
- 所有数变为 (0);
- 答案最小。
然而我们之前的做法并不能满足 (n >1) 的情况。对于这种情况的通用方法就是反悔贪心。
考虑到之前那个方程的性质,(x,y) 一定满足某种关系(看通解也看的出来吧)。当前的堆里面储存上当前的方程的解的贡献减去修改一次 (x,y) 之后的贡献。
为了让其满足所有数变为 (0),更改的时候考虑修改掉其 (x,y)。假设我们现在的 (sum x >0),我们取出操作次数最小的那个数,根据之前的方程通解增大 (x),减小 (y) 即可,然后再放回去。
这样,当 (sum x) 等于 (0) 的时候,答案就出来了,即为:
[sum_{i=1}^{n+1} dfrac{x_i+y_i}{2}
]
注意要除以 (2),而且要输出答案的时候除。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
void exgcd(LL &x,LL &y,LL a,LL b)
{
if(!b)
{
x=1,y=0;
return ;
}
exgcd(x,y,b,a%b);
LL tmp=x;
x=y;
y=tmp-a/b*y;
}
LL Abs(LL x){return x>0?x:-x;}
struct Answer{
LL x,y;
Answer(LL X=0,LL Y=0){x=X,y=Y;}
bool operator < (Answer ano) const {return Abs(x)+Abs(y)<Abs(ano.x)+Abs(ano.y);}
}p[100005];
struct Factor{
LL val,pos;
Factor(LL V=0,LL P=0){val=V,pos=P;}
bool operator < (Factor ano) const {return val>ano.val;}
};
priority_queue<Factor> Q;
LL n,a,b,s[100005];
int main(){
scanf("%lld %lld %lld",&n,&a,&b);
for(LL i=1;i<=n;++i) scanf("%lld",&s[i]);
LL d=gcd(a,b);
for(LL i=1;i<=n;++i) if(s[i]%d) return puts("-1")&0;
++n;
for(LL i=n;i;--i) s[i]=s[i]-s[i-1];
LL x,y;
exgcd(x,y,a,b);
a/=d,b/=d;
// printf("%lld %lld %lld %lld
",x,y,a,b);
// for(LL i=1;i<=n;++i) printf("%lld ",s[i]);
// puts("");
for(LL i=1;i<=n;++i)
{
LL sx=(x*s[i]/d%b+b)%b,sy=(s[i]/d-sx*a)/b;
p[i]=Answer(sx,sy);
sx-=b,sy+=a;
if(Answer(sx,sy)<p[i]) p[i]=Answer(sx,sy);
sy=(y*s[i]/d%a+a)%a,sx=(s[i]/d-sy*b)/a;
if(Answer(sx,sy)<p[i]) p[i]=Answer(sx,sy);
sx+=b,sy-=a;
if(Answer(sx,sy)<p[i]) p[i]=Answer(sx,sy);
}
// for(LL i=1;i<=n;++i) printf("%lld %lld
",p[i].x,p[i].y);
LL lc=0;
for(LL i=1;i<=n;++i) lc+=p[i].x;
lc/=b;
if(lc<0)
{
lc=-lc;
swap(a,b);
for(LL i=1;i<=n;++i) swap(p[i].x,p[i].y);
}
for(LL i=1;i<=n;++i) Q.push(Factor(Abs(p[i].x-b)+Abs(p[i].y+a)-Abs(p[i].x)-Abs(p[i].y),i));
for(LL i=1;i<=lc;++i)
{
Factor k=Q.top();
Q.pop();
LL now=k.pos;
p[now].x-=b,p[now].y+=a;
Q.push(Factor(Abs(p[now].x-b)+Abs(p[now].y+a)-Abs(p[now].x)-Abs(p[now].y),now));
}
LL ans=0;
for(LL i=1;i<=n;++i) ans+=(Abs(p[i].x)+Abs(p[i].y));
printf("%lld",ans/2);
return 0;
}