[BZOJ2792][Poi2012]Well
2792: [Poi2012]Well
Time Limit: 40 Sec Memory Limit: 64 MBSubmit: 137 Solved: 61
[Submit][Status][Discuss]
Description
给出n个正整数X1,X2,...Xn,可以进行不超过m次操作,每次操作选择一个非零的Xi,并将它减一。
最终要求存在某个k满足Xk=0,并且z=max{|Xi - Xi+1|}最小。
输出最小的z和此时最小的k。
Input
第一行两个正整数n, m (1<=n<=1,000,000, 1<=m<=10^18)。第二行n个正整数X1,X2,...Xn (Xi<=10^9)。
Output
输出k和z。数据保证方案一定存在。
Sample Input
16 15
8 7 6 5 5 5 5 5 6 6 7 8 9 7 5 5
8 7 6 5 5 5 5 5 6 6 7 8 9 7 5 5
Sample Output
1 2
HINT
将X序列变为
0 2 4 5 5 5 5 5 6 6 7 8 9 7 5 5
此时k=1,z=2,共操作了8+5+2=15次。
Source
要求最小化最大值,肯定想到二分。
二分判定的难点在于如何求最小的能等于0的数。
假如要把i位置变成0,则一定会修改一个区间[L,R]
所以必须满足a[L]>(i-L)*mid,且a[R]>(R-i)*mid
这两个显然都是单调的,然后[L,R]这个区间要想修改最小的话,最后一定变成前后两个等差数列,其中公差=mid,第i项=0,直接统计答案即可。
1 #include<cstdio> 2 #include<algorithm> 3 #define N 1000010 4 #define mid (l+r>>1) 5 #define ll long long 6 using namespace std; 7 int n,a[N],b[N]; 8 ll m,f[N],sum[N]; 9 inline int check(int x) 10 { 11 ll ans=0; 12 for(int i=1;i<=n;i++)b[i]=a[i]; 13 for(int i=2;i<=n;i++) 14 if(b[i]-b[i-1]>x) 15 ans+=b[i]-b[i-1]-x,b[i]=b[i-1]+x; 16 for(int i=n-1;i;i--) 17 if(b[i]-b[i+1]>x) 18 ans+=b[i]-b[i+1]-x,b[i]=b[i+1]+x; 19 for(int i=1;i<=n;i++) 20 sum[i]=sum[i-1]+b[i]; 21 if(ans>m)return 0; 22 for(int i=1,j=1;i<=n;i++) 23 { 24 while(j<i&&b[j]<=(ll)(i-j)*x)j++; 25 f[i]=sum[i-1]-sum[j-1]-(ll)(i-j)*(i-j+1)/2*x; 26 } 27 for(int i=n,j=n;i;i--) 28 { 29 while(j>i&&b[j]<=(ll)(j-i)*x)j--; 30 f[i]+=sum[j]-sum[i]-(ll)(j-i)*(j-i+1)/2*x; 31 } 32 for(int i=1;i<=n;i++) 33 if(b[i]+f[i]+ans<=m)return i; 34 return 0; 35 } 36 int main() 37 { 38 int l=0,r=0,ans=0; 39 scanf("%d%lld",&n,&m); 40 for(int i=1;i<=n;i++) 41 scanf("%d",&a[i]),r=max(r,a[i]); 42 while(l<=r) 43 { 44 if(check(mid))ans=mid,r=mid-1; 45 else l=mid+1; 46 } 47 printf("%d %d",check(ans),ans); 48 }
就让我永远不在这里写什么有意义的话--月下孤狼