题目大意:数轴上有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;
}