CSP - S 2026 T1

// T1 题解 赛时30min ac//
题面
题意 : 给你一个数组a,你可以增加其中一些数的值,增加的值之和不超过m,要使最后剩下的数组中,数值相同的数最多
分析 : 我们考虑最后剩下的数值相同的数是什么样的 经过简单思考,不难发现,要想让数最多,那么最后共有的数值一定在原数列中,进而想到先排序,考虑最后共同的值是谁,假设此时已经排好序,我们先对刚才的性质进行证明
性质1 : 最优的情况下,最终共同的值一定在原数组中
反证 : 假设最终共同的数不在原数组中 设它的值是val,最终变为这个值的位置构成集合S
不难发现 $$\forall i \in S, a[i] < val$$

  1. val > a[1] 则此时考虑把S中的数都变成a[n]

\[\because \forall i \in S, a[n] - a[i] < val - a[i] \]

\[\therefore \sum_{i \in S}^{n} val - a[i] > \sum_{i \in S}^{n} a[n] - a[i] \]

\[\therefore a[n]的改变方法更优 \]

  1. val > a[1] 不可能
  2. a[i] < val < a[i + 1] 同理,此时变为a[i], a[i + 1] 中的任意一个都更优

这样,我们已经大致有了思路 先排序 再枚举最终的值
此时我们贪心地考虑,若最后都变成a[i],我们一定先把接近a[i]的值变掉,于是,S集合事实上是一个连续的区间[l,i]
当我们递增地枚举i的时候 l也是递增的 双指针解决 复杂度 $$O(nlogn)$$代码如下:

//ac code
#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 5e5 + 5;

int n;
ll m, a[N], pre[N];//pre--前缀和

bool Can (int x, int y) {
    long long tmp = pre[y] - pre[x - 1];
    tmp = a[y] * (y - x + 1) - tmp;
    if(tmp <= m) return true;
    return false;
}//判断 区间[x,y]都变为y是否合法 O(1)

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    sort(a + 1, a + n + 1);
    for (int i = 1; i <= n; i ++) {
        pre[i] = pre[i - 1] + a[i];
    }
    int idx = 1, ans = 0;//idx代表最大的 j 使 [j,i]可以全部变成a[i]
    for (int i = 1; i <= n; i ++) {// 枚举最后共同的值
        while(!Can(idx, i) && idx <= i) {
            idx ++;
        }
        ans = max(ans, i - idx + 1);
    }
    cout << ans;
    return 0;
}

update on 2026.11.6

posted @ 2026-11-06 12:00  mzrltym  阅读(12)  评论(0)    收藏  举报