题解 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;
}
posted @ 2021-05-04 13:40  recollector  阅读(240)  评论(0编辑  收藏  举报