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

浙公网安备 33010602011771号