[关闭]
@ysner 2018-08-15T20:56:19.000000Z 字数 1493 阅读 1860

[HNOI2009]通往城堡之路

脑洞 贪心


题面

给一个长度为的序列。现可改变第个数的权值,使序列个数相差不超过。最小化权值改变大小之和。

解析

留坑待填

这题很奇妙咦
无解显然是

注意到,最终序列一定符合一个限制:
于是可以先把序列中所有值(除第一个)改为最小值,然后看怎么提高。

给一段区间同时升高米。
为了方便,假设区间内只有两种情况:
( 其实这个情况很好保证,只要扫一遍取)
如果第一种情况有个,第二种情况有个,则
于是我们的目标就是使同号可以为负)。

应用一下差分思想,发现可以用两个后缀区间表示一段区间。
于是就不用枚举右端点了。

然后可以发现,其实只要枚举左端点,在区间内取,然后给所有区间同时升高,以此类推,总能使答案更优。
并且只能升到,不能变得更大。
则答案最优时,
当然也要注意修改的合法性,如

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<algorithm>
  7. #include<vector>
  8. #define re register
  9. #define il inline
  10. #define ll long long
  11. #define max(a,b) ((a)>(b)?(a):(b))
  12. #define min(a,b) ((a)<(b)?(a):(b))
  13. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  14. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  15. using namespace std;
  16. const int mod=1e9+7,N=5050;
  17. ll n,d,a[N],b[N],ans;
  18. il ll gi()
  19. {
  20. re ll x=0,t=1;
  21. re char ch=getchar();
  22. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  23. if(ch=='-') t=-1,ch=getchar();
  24. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  25. return x*t;
  26. }
  27. int main()
  28. {
  29. re int T=gi();
  30. while(T--)
  31. {
  32. n=gi();d=gi();ans=0;
  33. fp(i,1,n) b[i]=a[i]=gi();
  34. if(abs(a[1]-a[n])>(n-1)*d) {puts("impossible");continue;}
  35. fp(i,2,n) b[i]=b[i-1]-d;
  36. while(a[n]^b[n])
  37. {
  38. re ll s=0,mn=1e18,mx=-1e18,pos,add=1e18;
  39. fq(i,n,2)
  40. {
  41. if(b[i]<a[i]) ++s,mn=min(mn,a[i]-b[i]);
  42. else --s;
  43. if(s>mx&&b[i-1]+d!=b[i]) mx=s,pos=i,add=mn;
  44. }
  45. add=min(add,b[pos-1]+d-b[pos]);
  46. fp(i,pos,n) b[i]+=add;
  47. }
  48. fp(i,1,n) ans+=abs(a[i]-b[i]);
  49. printf("%lld\n",ans);
  50. }
  51. return 0;
  52. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注