这道题是NOIP2011年普及组的一道题

但是从考察内容和难度来说,还是一道挺值得做的好题

思路分析

题目知识点考到了排序,最朴素的想法当然是用快速排序,每一轮都进行一次快排,然后写完果然爆T了。。。

再重新思考,发现这道题中,winner组和loser组,内部是顺序不变的

这句话如何理解呢?所有的winner,在这轮比赛前,是按某个顺序排列的,那么此轮结束后,每个winner都+1分,那么他们的相对顺序是不变的

而loser组也同理,loser本轮并不得分,所以这一轮前后,loser内部顺序不发生改变

于是,我们可以用3个数组来计算(我多写了一个tmp,写归并留下的习惯了,其实直接用p数组就好)

先读入所有的参赛者,用C++ STL自带的sort()进行第一轮排序

然后模拟比赛,把每对参赛者的胜者放入winner组,败者放入loser组,再用归并排序中的归并操作,把他们重新放回原数组

重复上述操作$ r $次后,即完成比赛过程的模拟

最后直接输出p[q - 1](排名第Q位参赛者)的编号

相关知识点

sort() - STL内置排序函数,省去我们手写一个快排的功夫了

归并排序 - 题目中只用到了归并排序中的'归并'操作,但是理解这个算法还是很有意义的

只要掌握了快速排序和归并排序的思想以及代码,并且有解决普通模拟题的编码能力,应该是可以解决掉这道题的

代码实现

// P1309 瑞士轮
// 6 Mar 2021 - zqsml
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 2e5 + 10;

struct person{
	int s, w, ind;
}p[N];

bool cmp(const person &a, const person &b){
	if(a.s != b.s)
		return a.s > b.s;
	return a.ind < b.ind;
}

person winner[N], loser[N], tmp[N];

int n, r, q;

int main(){
	scanf("%d %d %d", &n, &r, &q);
	for(int i = 0; i < 2 * n; i ++ ) p[i].ind = i + 1;
	for(int i = 0; i < 2 * n; i ++ ) scanf("%d", &p[i].s);
	for(int i = 0; i < 2 * n; i ++ ) scanf("%d", &p[i].w);
	sort(p, p + 2 * n, cmp);

	while(r -- ){
		for(int i = 0; i < n; i ++ ){
			if(p[i * 2].w < p[i * 2 + 1].w){
				p[i * 2 + 1].s ++ ;
				loser[i] = p[i * 2];
				winner[i] = p[i * 2 + 1];
			}
			else{
				p[i * 2].s ++ ;
				winner[i] = p[i * 2];
				loser[i] = p[i * 2 + 1];
			}
		}
		// p1 for winner, p2 for loser
		int p1 = 0, p2 = 0;
		int k = 0;
		while(p1 < n && p2 < n){
			if(winner[p1].s > loser[p2].s || (winner[p1].s == loser[p2].s && winner[p1].ind < loser[p2].ind)){
				tmp[k ++ ] = winner[p1 ++ ];
			}
			else tmp[k ++ ] = loser[p2 ++ ];
		}
		while(p1 < n) tmp[k ++ ] = winner[p1 ++ ];
		while(p2 < n) tmp[k ++ ] = loser[p2 ++ ];
		for(int i = 0; i < 2 * n; i ++ ) p[i] = tmp[i];
	}

	printf("%d\n", p[q - 1].ind);
	return 0;
}