题解:P3202 [HNOI2009] 通往城堡之路
liyanchen_1 · · 题解
这题我不会做
在学习了几篇大佬的题解后,终于 AC 了这道题,来发篇题解记录一下。
题目传送门
题意简述
给定一个数列,你可以花费
分析
我们可以认为很多次的对单个元素的操作可以合并成对一个区间的操作。
首先我们先考虑无解的情况:
证明:这是显然的吧。
然后我们考虑先构造一组解,使得可以剔除掉减少数列元素大小的这个操作。
于是我们可以构造数组
我们可以发现这样构造之后的
先来思考怎么处理操作区间。
引理:若干次区间操作都可以转化成对
证明:如果我们在区间
然后来思考每次增加的高度
将区间
所以我们可以直接贪心的想令
证明:设
1:若
2:若
综上,贪心策略正确。
代码
#include<bits/stdc++.h>
using namespace std;
#define INF 2147483647
#define LLINF 0x3f3f3f3f3f3f3f3fLL
#define ft first
#define sd second
using ci=const int;
using ll=long long;
using ld=double;
using ull=unsigned long long;
using lli=__int128;
int T;
ll n,d;
ll a[5005];
ll b[5005];
signed main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
ll i,j;
ios::sync_with_stdio(false),cin.tie(0);
cin>>T;
while(T--)
{
cin>>n>>d;
for(i=1;i<=n;i++)
{
cin>>a[i];
}
if(labs(a[1]-a[n])>(ll)d*(n-1))
{
cout<<"impossible\n";
continue;
}
b[1]=a[1];
for(i=2;i<=n;i++)
{
b[i]=b[i-1]-d;
}
while(b[n]!=a[n])
{
ll c=LLINF,ms=-LLINF,h,id,s=0;
for(i=n;i>1;i--)
{
if(b[i]<a[i]) s++,c=min(c,a[i]-b[i]);
else s--;
if(b[i]!=b[i-1]+d&&s>ms)
{
id=i;
h=c;
ms=s;
}
}
h=min(h,b[id-1]-b[id]+d);
for(i=id;i<=n;i++) b[i]+=h;
}
ll ans=0;
for(i=1;i<=n;i++)
{
ans+=labs(a[i]-b[i]);
}
cout<<ans<<'\n';
}
return 0;
}