2016 Pacific Northwest Region Programming Contest—Division 1 K Tournament Wins

2016 Pacific Northwest Region Programming Contest—Division 1 K Tournament Wins

七月 30, 2021

题目概述

某人参加一场淘汰赛,实力排名为$r$,排名越高实力越强。一共有$2^k$支队伍参加比赛,问这个人获胜场次的概率。

数据范围

分析与解

这题还是一个要画画图,不能干想。其实把那张满二叉树的晋级图画出来,就明白是怎么回事了。

其实从图上显然看出,某人如果要至少赢$i$​​​场,则应该战胜$2^i-1$个对手。这其实和线段树求区间最小值的原理是类似的。因此可以得到概率

设$f(i)$表示恰好赢$i$场的概率,那么答案

这里值得一提的是怎么快速计算$s[i]$,答案是取对数。

即

而

因此只要预处理即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <bits/stdc++.h>
using namespace std;
int mread() {
int ret=0,f=1;
char c=getchar();
while(c<'0' || c>'9') {
if(c=='-')f=-1;
c=getchar();
}
do {
ret=ret*10+c-'0';
c=getchar();
} while('0'<=c && c<='9');
return ret*f;
}
int r,k;
const int N = (1<<20)+50;
double my_pow[N],s[30];
double lnc(int n,int m) {
return my_pow[n] - my_pow[m] - my_pow[n-m];
}
int main() {
int n,now;
double ans=0.0;
k=mread(),r=mread();
for(int i=1; i<=(1<<k); i++)my_pow[i]=my_pow[i-1]+log((double)1.0*i);
n=(1<<k);
for(int i=1; i<=k; i++)
{
now=(1<<i)-1;
if(n-r < now)continue;
s[i]=exp(lnc(n - r,now) - lnc(n - 1,now));
}
for(int i=1; i<=k; i++)
ans+=s[i];
printf("%.5lf\n",ans);
return 0;
}