CSP歷年複賽題-P1309 [NOIP2011 普及組] 瑞士輪

江城伍月發表於2024-05-30

原題連結:https://www.luogu.com.cn/problem/P1309

題意解讀:2n個人,每次按分數從大到小排名,相鄰兩人比賽,勝者得1分,r輪後,第q個人是誰。

解題思路:

如果直接使用模擬法,每輪比賽後都進行一次排序,總的時間複雜度是r*2n*log2n,大概在50*2*10^5*18 > 10^8,會超時,不過大概可以得到50分。

再進一步思考,每一次比賽前,已經按照分數大到小排好序,每一輪比賽,總會有n個勝利,n個人失敗,勝利的n個人都加1分,加1分後勝者的n個人相對排序不變,失敗的n個人相對排序也不變!

因此,容易想到,可以針對每輪比賽的勝者、敗者這兩個有序的序列進行歸併,合併成一個有序序列,而歸併的時間複雜度為O(n),總複雜度在r*O(n)。

100分程式碼:

#include <bits/stdc++.h>
using namespace std;

const int N = 100005;
int n, r, q;
struct node
{
    int id; //編號
    int s; //分數
    int w; //實力值
};
node a[2 * N], b[N], c[N]; //b是每次比賽的勝者組,c是每次比賽的敗者組
bool cmp(node a, node b)
{
    if(a.s == b.s) return a.id < b.id;
    return a.s > b.s; 
}

int main()
{
    cin >> n >> r >> q;
    for(int i = 1; i <= 2 * n; i++) cin >> a[i].s, a[i].id = i;
    for(int i = 1; i <= 2 * n; i++) cin >> a[i].w;
    
    sort(a + 1, a + 2 * n + 1, cmp); //按選手的分數從大到小排序
    while(r--) //進行r輪比賽
    {
        int cnt = 0;
        for(int i = 1; i <= 2 * n - 1; i+=2) //列舉每一對比賽選手
        {
            //對勝者進行加分,並將勝者加入勝者組,敗者加入敗者組
            if(a[i].w > a[i+1].w) a[i].s++, b[++cnt] = a[i], c[cnt] = a[i+1];
            else a[i+1].s++, b[++cnt] = a[i+1], c[cnt] = a[i];
        }
        //對勝者組、敗者組進行歸併
        int i = 1, j = 1, k = 1;
        while(i <= cnt && j <= cnt)
        {
            if(cmp(b[i], c[j])) a[k++] = b[i++];
            else a[k++] = c[j++];
        }
        while(i <= cnt) a[k++] = b[i++];
        while(j <= cnt) a[k++] = c[j++];
    }

    cout << a[q].id;
    
    return 0;
}

相關文章