CSP历年复赛题-P1309 [NOIP2011 普及组] 瑞士轮
原题链接: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;
}