「POI2012」Leveling Ground

判断有无解,显然就是判断是否 (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;
}
原文地址:https://www.cnblogs.com/amagaisite/p/13922595.html