题解 P7584 [COCI2012-2013#1] F7
题目描述
有 \(N\) 位选手参加一个比赛。每个回合,第一名会得到 \(N\) 分,第二名会得到 \(N - 1\) 分,以此类推,最后一名会得到 \(1\) 分。
现在第 \(i\) 位选手初始有 \(B_i\) 分。求多少选手经过一个回合,分数有机会变成所有选手中最高的。
\(3 \leq n \leq 10^5 ,1 \leq B_i \leq 2\times 10^6\) 。
Solution
提供一个不一样的做法我太菜了想不到普通贪心。
首先将 \(B\) 从大到小排序,然后对选手按 \(B_i\) 从大到小重新编号。
首先 \(1\) 号选手一定能成为最高的,因为它本身是最大的,加的数字也可以是最大的,其他的数字两个加数都比它小,所以它是最大的。
对于其他的 \(B_i\) ,想要让它成为最大的,我们必须要创造一个最优局面:
- \(i\) 的最终得分一定是 \(B_i+n\) ,也就是说这个回合 \(i\) 一定是第一名。
- 我们要让剩下的选手的最大得分尽可能小,也就是说,要让 \(1\) 号选手这回合是最后一名, \(2\) 号选手是倒数第二名 \(\cdots\) 。当然如果碰到了 \(i\) 号选手,跳过去就行了。最后一个非 \(i\) 号选手的选手得分是 \(B_i+n-1\) 。
然后找找规律,例如:下面是 \(B=\{5,4,3,2,1\}\) 时,\(i\) 分别为 \(2,3\dots 5\) 时的最优局面:
2 : 5 + 1 ,4 + 5 ,3 + 2 ,4 + 3 ,1 + 4
3 : 5 + 1 ,4 + 2 ,3 + 5 ,4 + 3 ,1 + 4
4 : 5 + 1 ,4 + 2 ,3 + 3 ,4 + 5 ,1 + 3
5 : 5 + 1 ,4 + 2 ,3 + 3 ,4 + 4 ,1 + 5
我们会惊奇的发现: \(i\) 号选手的最优局面和 \(i+1\) 号选手的最优局面只有两个位置不同,并且区别就是 \(i\) 号选手的得分 \(B_i+n\to B_i+i\) , \(i+1\) 号选手的得分: \(B_{i+1}+(i+1)\to B_{i+1}+n\) 。
这样就可以维护一个 multiset ,然后现在里面插入 \(2\) 号选手的最优局面下的得分,然后判断 \(2\) 号选手是不是最高得分的那个,然后修改局面为 \(3\) 号选手的最优局面 \(\cdots\) 。
思路就是这样,代码也很好写。注意对于 \(n\) 号选手不需要修改局面了。
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <iostream>
#include <queue>
#include <set>
using namespace std;
inline int read() {
int num = 0 ,f = 1; char c = getchar();
while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
return num * f;
}
inline bool cmp(int a ,int b) {return a > b;}
const int N = 3e5 + 5;
int a[N] ,n ,ans = 1;
multiset <int> st;
signed main() {
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
sort(a + 1 ,a + n + 1 ,cmp);
st.insert(a[1] + 1);
st.insert(a[2] + n);
for (int i = 3; i <= n; i++) st.insert(a[i] + i - 1);
for (int i = 2; i < n; i++) {
int c = *--st.end();
if (c == a[i] + n) ans++;
multiset <int>::iterator it = st.find(a[i] + n);
st.erase(it);
st.insert(a[i] + i);
it = st.find(a[i + 1] + i);
st.erase(it);
st.insert(a[i + 1] + n);
}
int c = *--st.end();
if (c == a[n] + n) ans++;
printf("%d\n" ,ans);
return 0;
}