题目大意:数轴上有n个点,在总代价不超过m的情况下,至多可以将多少个点移到一起?(代价:每个点移动的距离)
数据范围500万,递增输入,不需要排序,显然需要用O(n)做法才能满分,而且读入还可能被卡。
能走到一起的,必然是连续的若干个点:如果一个区间中有个点没有移动过去,那么终点要么在他左边,要么在他右边;如果在左边,他右边的点移动过去的代价比他大,没必要;另外的方向同理。
区间平移:至少1各点,区间至少为2才判断;从区间[i=1, j=2]开始,如果能移到一起,那么j加1,尝试让更多的点移到一起;如果不能移到一起,那么左端点无论如何都无法跟j以及j右边的点移到一起了,i加1;边界:i跟j相等,让j加1,保存区间长度至少2;区间右端点超过n结束。
【验证】
1、贪心选位置:区间所有点移到同一个位置,如果是奇数个点,必然移到中间那个点的位置最好。中间点左边有x各点,右边也有x个点,如果位置左移,代价左边减少x,右边增加x+1,这个1的中间点本身;如果左边点转到右边去,代价会更大。同样的,如果是偶数个点,移到中间那两个点之间的位置都可以,证明同上。
2、求代价之和:对于区间[l, r],不妨设选中的点是k,位置是a[k],左边的代价是a[k]-a[l] + a[k]-a[l+1] ……,共k-l+1个,可以转成a[k]*(k-l+1)加上[l, k]的区间和;同样的,右边也可以这样做。
#include <bits/stdc++.h>
#define LL long long
#define N 5000005
using namespace std;
LL n, m, i, j, k, ans=1, a[N], s[N];
LL du(){
LL n = 0;
char c = getchar();
while(c <= 32) c = getchar();
while(c > 32){
n = n * 10 + c - 48;
c = getchar();
}
return n;
}
int ok(LL l, LL r){
LL i, j, k, x, y, d;
k = l+r >> 1;
x = k - l + 1, y = r - k;
d = x*a[k] - (s[k]-s[l-1]);
d += (s[r]-s[k]) - y*a[k];
return d <= m;
}
int main(){
scanf("%lld%lld", &n, &m);
for(i=1; i<=n; i++){
// scanf("%lld", &a[i]);
a[i] = du();
s[i] = s[i-1] + a[i];
}
for(i=1, j=2; j<=n; ){
if(ok(i, j)){
if(++j-i > ans) ans = j-i;
}
else if(++i == j) j++;
}
printf("%lld\n", ans);
return 0;
}


京公网安备 11010502036488号