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;
}