luogu P1309 瑞士轮【排序】
题目背景
在双人对决的竞技性比赛,如乒乓球、羽毛球、国际象棋中,最常见的赛制是淘汰赛和循环赛。前者的特点是比赛场数少,每场都紧张刺激,但偶然性较高。后者的特点是较为公平,偶然性较低,但比赛过程往往十分冗长。
本题中介绍的瑞士轮赛制,因最早使用于1895年在瑞士举办的国际象棋比赛而得名。它可以看作是淘汰赛与循环赛的折中,既保证了比赛的稳定性,又能使赛程不至于过长。
题目描述
2*N 名编号为 1~2N 的选手共进行R 轮比赛。每轮比赛开始前,以及所有比赛结束后,都会按照总分从高到低对选手进行一次排名。选手的总分为第一轮开始前的初始分数加上已参加过的所有比赛的得分和。总分相同的,约定编号较小的选手排名靠前。
每轮比赛的对阵安排与该轮比赛开始前的排名有关:第1 名和第2 名、第 3 名和第 4名、……、第2K – 1 名和第 2K名、…… 、第2N – 1 名和第2N名,各进行一场比赛。每场比赛胜者得1 分,负者得 0 分。也就是说除了首轮以外,其它轮比赛的安排均不能事先确定,而是要取决于选手在之前比赛中的表现。
现给定每个选手的初始分数及其实力值,试计算在R 轮比赛过后,排名第 Q 的选手编号是多少。我们假设选手的实力值两两不同,且每场比赛中实力值较高的总能获胜。
输入输出格式
输入格式:
输入文件名为swiss.in 。
输入的第一行是三个正整数N、R 、Q,每两个数之间用一个空格隔开,表示有 2*N 名选手、R 轮比赛,以及我们关心的名次 Q。
第二行是2*N 个非负整数s1, s2, …, s2N,每两个数之间用一个空格隔开,其中 si 表示编号为i 的选手的初始分数。 第三行是2*N 个正整数w1 , w2 , …, w2N,每两个数之间用一个空格隔开,其中 wi 表示编号为i 的选手的实力值。
输出格式:
输出文件名为swiss.out。
输出只有一行,包含一个整数,即R 轮比赛结束后,排名第 Q 的选手的编号。
输入输出样例
数据范围
对于30% 的数据,1 ≤ N ≤ 100;
对于50% 的数据,1 ≤ N ≤ 10,000 ;
对于100%的数据,1 ≤ N ≤ 100,000,1 ≤ R ≤ 50,1 ≤ Q ≤ 2N,0 ≤ s1, s2, …, s2N≤10^8,1 ≤w1, w2 , …, w2N≤ 10^8。
noip2011普及组第3题。
冷静分析
最暴力的肯定是每battle一次都进行一次结构体快速排序,但是太慢了,60分。
经我们冷静分析可以发现一些特殊性质,也就是说,一次battle下来,因为有之前的“积分有序性”,所以新的排名并不会有太大的变化。卡sort的情况下,我们可以考虑稳定的归并,O(nlogn);
这里使用了一种类mergesort;
注释写在代码里。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 int n,r,e; 6 struct node 7 { 8 int score,pos,abi; 9 }; 10 node q[200002],win[200002],lose[200002];//win和lose保证有序。 11 bool cmp(node a,node b) 12 { 13 if(a.score==b.score) return a.pos<b.pos; 14 return a.score>b.score; 15 } 16 void merge() 17 {//类归并 18 int tmp=n/2;//注意。win和lose中元素个数各为tmp个 19 int i=1,j=1,k=1; 20 while(i<=tmp&&j<=tmp) 21 { 22 if(win[i].score>lose[j].score||(win[i].score==lose[j].score&&win[i].pos<lose[j].pos)) 23 { 24 q[k]=win[i]; 25 i++;k++; 26 } 27 else 28 { 29 q[k]=lose[j]; 30 j++;k++; 31 } 32 }//下面的while循环,要么执行第一个,要么执行第二个,因为当 上一循环跳出时总有一条件不符合。 33 while(i<=tmp) 34 { 35 q[k]=win[i]; 36 i++;k++; 37 } 38 while(j<=tmp) 39 { 40 q[k]=lose[j]; 41 j++;k++; 42 } 43 } 44 int main() 45 { 46 scanf("%d%d%d",&n,&r,&e); 47 n=2*n;//小细节 48 for(int i=1;i<=n;i++) 49 { 50 scanf("%d",&q[i].score); 51 q[i].pos=i;//pos代表初始编号 52 } 53 for(int i=1;i<=n;i++) scanf("%d",&q[i].abi); 54 sort(q+1,q+n+1,cmp); 55 for(int i=1;i<=r;i++) 56 {//num在表示第几次battle,及其结果。 57 int num=0;//注意num是用于一次battle下来的,一次battle要走完下面的循环,注意定义位置。 58 for(int j=1;j<=n;j+=2)//步长加至2. 59 { 60 if(q[j].abi>q[j+1].abi) 61 { 62 q[j].score ++; 63 num++; 64 win[num]=q[j];//第num个赢的人 65 lose[num]=q[j+1];//第num个输的人 66 } 67 else 68 { 69 q[j+1].score ++; 70 num++; 71 win[num]=q[j+1]; 72 lose[num]=q[j]; 73 } 74 } 75 merge();//两两battle后统一排序 76 } 77 printf("%d",q[e].pos); 78 return 0; 79 }
大道至简!