DTOJ 6316 沙丘 题解

DTOJ 6316 沙丘 题解

题面:

http://59.61.75.5:8018/p/P6316

在满天的星光下,灰大狼一人孤独地堆起了小沙丘。有 \(n\) 堆沙丘,每堆沙丘有相对高度 \(h_i\),每次灰大狼可以选择一段连续的沙丘并将它们高度增加或减少 \(a\)\(b\),其中 \(a,b\) 给定,灰大狼想知道最少要几次才能把所有沙丘的相对高度都变成 \(0\)。如果无解请输出 -1

题解

首先看到是 “一段连续的沙丘”,果断考虑差分.

差分完增加或减小 \(a\)\(b\) 可以被转化成两个位置上分别 \(+a, -a\)\(+b, -b\)

最后每个位置上一定是 \(xa+yb\) 的形式,记差分数组为 \(\{d_i\}\),则 \(xa+yb=d_i\)

显然是可以 exgcd 求出一组特解 \(x_0,y_0\)

于是通解可以表示成 \(\begin{cases}x=x_0+\frac{b}{\gcd(a,b)}\cdot k\\ y=y_0-\frac{a}{\gcd(a,b)}\cdot k\end{cases}\)

最后答案即 \(\frac{1}{2}\sum(|x|+|y|)\)

测试的时候我直接调整每个 \(x,y\) 使 \(|x|+|y|\) 最小.

\(|x|+|y|=|x_0+\frac{b}{\gcd(a,b)}\cdot k|+ |y_0-\frac{a}{\gcd(a,b)}\cdot k|\)

根据高一数学,这一定是三个一次函数组成的分段函数.

于是最小值一定只有四种情况, \(x\) 最小正值,\(x\) 最大负值, \(y\) 最小正值,\(y\) 最大负值.

但是我们发现这么做有个问题,就是差分数组要满足 \(\sum x = 0\)\(\sum y = 0\)

上次数竞比赛也是没注意到限制条件少了50分ww

考虑继续调整,注意到 \(\sum x =0 \Rightarrow \sum y =0\) 所以只需要调整 \(\sum x = 0\)

不妨设 \(\sum x > 0\) 我们对于一个位置 \(i\),若 \(\sum x\) 变化 \(1\), 则 \(|x|+|y|\) 变化 \(|x-\frac{b}{gcd(a,b)}|-|x|\)

把所有的变化存进堆里,从小到大取出来.

嗯这就做完了 \((\land \omega\land)\)

代码好难写(码力不够

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+5;
int rd()
{
	int x,f=0; char ch;
	while(!isdigit(ch=getchar())) if(ch=='-') f=1;
	for(x=(ch^48); isdigit(ch=getchar()); x=(x<<1)+(x<<3)+(ch^48));
	return f?-x:x;
}
int n,a,b,c[N],x[N],y[N];
priority_queue<pair<int,int> > Q;
int exgcd(int a,int b,int &x,int &y)
{
	if(!b) { x=1,y=0; return a; }
	int d=exgcd(b,a%b,y,x); y-=(a/b)*x;
	return d;
}
int main() 
{
	n=rd(),a=rd(),b=rd();
	for(int i=1; i<=n; i++) c[i]=rd();
	for(int i=++n; i; --i) c[i]=c[i]-c[i-1];
	int tx,ty,d=exgcd(a,b,tx,ty); a/=d,b/=d;
	for(int i=1; i<=n; i++)
	{
		if(c[i]%d) { puts("-1"); return 0; }
		int X=((ll)tx*c[i]/d%b+b)%b,Y=(c[i]/d-(ll)a*X)/b;
		x[i]=X,y[i]=Y;
		X-=b,Y+=a;
		if(abs(X)+abs(Y)<abs(x[i])+abs(y[i])) x[i]=X,y[i]=Y;
		Y=((ll)ty*c[i]/d%a+a)%a,X=(c[i]/d-(ll)b*Y)/a;
		if(abs(X)+abs(Y)<abs(x[i])+abs(y[i])) x[i]=X,y[i]=Y;
		Y-=a,X+=b;
		if(abs(X)+abs(Y)<abs(x[i])+abs(y[i])) x[i]=X,y[i]=Y;
	}
	ll s=0;
	for(int i=1; i<=n; i++) s+=x[i];
	s/=b;
	if(s<0) s=-s,swap(a,b),swap(x,y);
	auto f = [&](const int &p){ return abs(x[p]-b)+abs(y[p]+a)-abs(x[p])-abs(y[p]); };
	for(int i=1; i<=n; i++) Q.push(make_pair(-f(i),i));
	while(s--)
	{
		int p=Q.top().second; Q.pop();
		x[p]-=b,y[p]+=a;
		Q.push(make_pair(-f(p),p));
	}
	ll ans=0;
	for(int i=1; i<=n; i++) ans+=abs(x[i])+abs(y[i]);
	printf("%lld\n",ans/2);
	return 0;
}
posted @ 2022-11-13 18:47  copper_carbonate  阅读(28)  评论(0编辑  收藏  举报