1 条题解
-
0
自动搬运
来自洛谷,原作者为

MloVtry
**搬运于
2025-08-24 21:46:26,当前版本为作者最后更新于2017-10-26 19:50:14,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
【膜拜解决此题的远古神犇】
首先,我们构造出一组【可行解】,也就是以a[i]为初始高度,后面每一级都下降d个高度(此时不考虑a[n]不变的条件)
我们可以肯定,最终答案一定是在这个【(伪)解】基础上,让一些区间升高得到的
那么我们现在就要升高一些区间
(当前【可行解】用b数组来表示)
假定我们让一个区间升高h,并且满足b[i]+h<=a[i],那么,假定这段区间有s1个点,b[i]<a[i],s2个点,b[i]>=a[i],则ans-=(s1*h);ans+=(s2*h)
也就是说,此时让这段区间升高h,答案会更优
那么我们每次寻找一段区间,使其加上这个h
当然,前提是满足这个区间的b[i]+h<=a[i],也就是h=min(a[i]-b[i])
【当然还有奇奇怪怪的小边界咯,这里并不会说,自己推一推吧!】
我们可以发现,b[n]这个点,一定是需要升高的,并且升高到a[n]之后就不能再升高
那么我们寻找一个后缀区间,则至少会增高b[n]这个节点
假设通过上述操作,现在b[n]已经等于a[n],这个解法就是最优的
1.任何一段区间再增高,不会使答案更优
首先设该区间为[i,j],既然该区间能够升高,那么则必有s1*h>s2*h--->s1>s2
假定[j+1,n]增加过,那么在枚举后缀时,是不会遗漏的,所以此时[i,j]不增高是不可能的
假定[j+1,n]没有增加过,那么,b[j]与b[j+1]的差值必定为d,一旦增加b[j],则解必定不合法
2.任何一段区间再降低,不会使答案更优
设区间为[i,j]
假定[i,j]增高过,那么降低就相当于增高的反操作,也就是撤销增高---既然撤销更优,那么我们根本就不会增加它
假定[i,j]没有增高过,那么他们就必定不能降低---因为初始的b[i~j]已经是可行解的最低高度了
由此得证
代码
#define ll long long #define inf 1LL<<50 #include<iostream> #include<cstdio> #include<cmath> #define N 5010 using namespace std; int n,T; ll d; ll a[N],b[N]; ll ans; int main() { scanf("%d",&T); while(T--) { scanf("%d%lld",&n,&d); ans=0; for(int i=1;i<=n;++i) scanf("%lld",&a[i]); b[1]=a[1]; for(int i=2;i<=n;++i) b[i]=b[i-1]-d; if(abs(a[n]-a[1])>(ll)d*(n-1)) {puts("impossible");continue;} while(b[n]!=a[n]) { ll tm=-inf,at,add,val=inf,s=0; for(int i=n;i>1;--i) { if(b[i]<a[i]) s++,val=min(val,a[i]-b[i]); else s--; if(s>tm&&b[i]!=b[i-1]+d) tm=s,at=i,add=val; } add=min(add,b[at-1]+d-b[at]); for(int i=at;i<=n;++i) b[i]+=add; } for(int i=1;i<=n;++i) ans+=abs(a[i]-b[i]); printf("%lld\n",ans); } return 0; }
- 1
信息
- ID
- 2275
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者