AC通道:http://cojs.tk/cogs/problem/problem.php?pid=1724
BTP职业网球赛
Description
参加职业网球赛的奶牛们有着职业牛网球赛协会(BTP)的排名。
有时候,预测一场网球赛的结果是可能的。
如果参赛的两头牛排名之间的差距大于一个给定的常数K(0<=K<=N-1),即|Rank1-Rank2|>K(其中Rank1,Rank2分别表示奶牛1与奶牛2的排名),那么排名较高的奶牛总是会赢得比赛的胜利。
下周将有一个大型的淘汰赛制的赛事,有N(N=2^t,t<=16,t∈N)头奶牛参赛,产生一个冠军。在第一轮,N/2对选手进行比赛,获胜的N/2个选手进入下一轮。
同样,下面的每轮比赛中,都是获胜的一半进入下一轮,直到只剩一头牛。
场外的牛们在对比赛下赌注,想知道随着一轮一轮的比赛,最后有可能夺冠的牛中排名最低的牛的排名。
你的工作就是计算这个最低排名,并且给出一种能使这头牛获胜的场次安排。
Input
第1行:两个空格隔开的数N和K。
Output
一行一个整数,即所有可能夺冠的牛中排名最低的牛的排名。
Sample Input
16 3
Sample Output
11
Solution
本题首先可以想到二分答案。这种求极值的问题一般都可以通过二分答案转化为判定性的问题。
在判定是否可以实现的时候,我们可以想到一种贪心的方法,进行倒退,对于当前轮未被刷下去的奶牛,找到一个被刷下去的,标号最小的它能够战胜的奶牛。如何找到这个奶牛呢?一开始,我直接用的循环,结果超时了(>﹏<),接着,我突然想到了并查集中,可以用find(i)表示从i开始未被刷的奶牛。
接着就可以愉快的AC了。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int t,k,n,l=1,r,ans=0,tot;
int fa[100000];
bool in[100000];
int num[100000];
inline int Max(int x,int y){
return x>y?x:y;
}
inline int Min(int x,int y){
return x<y?x:y;
}
int find(int x){
int tmp=x,pre;
while(tmp!=fa[tmp])tmp=fa[tmp];
while(x!=tmp){
pre=fa[x];
fa[x]=tmp;
x=pre;
}
return tmp;
}
bool pan(int x){
in[x]=true;
num[1]=x;
fa[x]=x+1;
int cnts=1,rcnts=1;
for(int step=1;step<=t;step++){
for(int i=1;i<=cnts;i++){
int ks=num[i];
int j=find(Max(1,ks-k));
for(;j<=n;j=find(j))if(j!=ks&&!in[j]&&j!=x){
if(j==0)return false;
fa[j]=j+1;
tot++;
in[j]=true;
num[++rcnts]=j;
break;
}
}
cnts=rcnts;
}
return true;
}
int main(){
freopen("btp.in","r",stdin);
freopen("btp.out","w",stdout);
scanf("%d%d",&n,&k);
int tmp=n;
r=n;
while(!(tmp&1)){tmp/=2;t++;}
while(l<=r){
int mid=(l+r)/2;
memset(in,0,sizeof in);
for(int i=1;i<=n;i++)fa[i]=i;
bool flag=pan(mid);
if(flag){
if(mid>ans)ans=mid;
l=mid+1;
}
else{
r=mid-1;
}
}
tot++;
printf("%d\n",ans);
return 0;
}