题目来源:https://www.luogu.org/problem/show?pid=1309
题目描述:
2*N 名编号为 1~2N 的选手共进行R 轮比赛。每轮比赛开始前,以及所有比赛结束后,都会按照总分从高到低对选手进行一次排名。选手的总分为第一轮开始前的初始分数加上已参加过的所有比赛的得分和。总分相同的,约定编号较小的选手排名靠前。
每轮比赛的对阵安排与该轮比赛开始前的排名有关:第1 名和第2 名、第 3 名和第 4名、……、第2K – 1 名和第 2K名、…… 、第2N – 1 名和第2N名,各进行一场比赛。每场比赛胜者得1 分,负者得 0 分。也就是说除了首轮以外,其它轮比赛的安排均不能事先确定,而是要取决于选手在之前比赛中的表现。
现给定每个选手的初始分数及其实力值,试计算在R 轮比赛过后,排名第 Q 的选手编号是多少。我们假设选手的实力值两两不同,且每场比赛中实力值较高的总能获胜。
解析:这题可以考虑排序,第一轮直接排序,求出结果。接着我们注意到,每一轮必定会产生n个胜利者得分,n个失败者不得分,这两组各自内部的排名是不变的。我们可以考虑把他们分开处理。则只需要合并的操作。时间复杂度为O(nlogn+n*r)
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<cctype> 8 #include<iomanip> 9 using namespace std; 10 int n,r,q; 11 struct node{ 12 int w,s; 13 int ni; 14 }wh[200001],win[200001],lose[200001]; 15 bool cmp(node a,node b){ 16 if(a.s==b.s) return a.ni<b.ni; 17 return a.s>b.s; 18 } 19 void work(int n,int r){ 20 int i,j,k; 21 for(j=1;j<=r;j++){ 22 int wi=1,lo=1; 23 for(i=1;i<=n;i=i+2){ 24 if(wh[i].w>wh[i+1].w){ 25 win[wi]=wh[i]; 26 wi++; 27 lose[lo]=wh[i+1]; 28 lo++; 29 } 30 if(wh[i].w<wh[i+1].w){ 31 win[wi]=wh[i+1]; 32 wi++; 33 lose[lo]=wh[i]; 34 lo++; 35 } 36 } 37 wi--; 38 lo--; 39 for(i=1;i<=n/2;i++) win[i].s++; 40 int x=1,y=1; 41 i=1; 42 while(x<=n/2 && y<=n/2){ 43 if(win[x].s>lose[y].s){ 44 wh[i++]=win[x++]; 45 } 46 if(win[x].s<lose[y].s){ 47 wh[i++]=lose[y++]; 48 } 49 if(win[x].s==lose[y].s && win[x].ni<lose[y].ni){ 50 wh[i++]=win[x++]; 51 } 52 if(win[x].s==lose[y].s && win[x].ni>lose[y].ni){ 53 wh[i++]=lose[y++]; 54 } 55 } 56 if(x>n/2) 57 while(y<=n/2) 58 wh[i++]=lose[y++]; 59 else 60 while(x<=n/2) 61 wh[i++]=win[x++]; 62 } 63 } 64 int main() 65 { 66 int i,j,k; 67 cin>>n>>r>>q; 68 n*=2; 69 for(i=1;i<=n;i++){ 70 cin>>wh[i].s; 71 wh[i].ni=i; 72 } 73 for(i=1;i<=n;i++) 74 cin>>wh[i].w; 75 sort(wh+1,wh+n+1,cmp); 76 work(n,r); 77 cout<<wh[q].ni<<endl; 78 return 0; 79 }