CF859D Third Month Insanity

洛咕

题意:有\(2^n\)个人要进行比赛,每次\(2i\)\(2i+1\)号人进行比赛.这一轮中赢的人进入下一轮.下一轮比赛的时候把进入这一轮的人按编号排好,仍然是像之前那样相邻的进行一次比赛.最后只剩下一个人.数据给出对于$ x,y\(,\)x\(打赢\)y\(的概率.第\)i$轮比赛会角逐出\(2^{n-i}\)个赢家.我们要在比赛开始前,猜每轮的赢家(一轮的赢家一定要是上一轮的赢家).第$ i $轮每猜中一个赢家就会得到\(2^{i-1}\)的得分.求最大的期望得分.\(n<=6\)

分析:不难发现整个比赛其实是一棵完全二叉树,树的第i层代表第i轮比赛,树上的每个节点都代表一个区间(这一轮的参赛人的编号).

所以我们考虑在树上dfs,设\(w[i][j]\)表示在以i号节点为根的树中(即在第i轮比赛中)j获胜的概率.设\(f[i][j]\)表示在以i号节点为根的树中(即在第i轮比赛中)j获胜的最大期望得分.

对于w数组的转移,我们只要从左子树和右子树中分别选一个节点,然后结合概率来算就好了.对于f数组的转移,一定要在w数组转移之后,同样是从左子树和右子树中分别选一个节点,然后算期望就好了.具体的转移方程直接看代码吧,很好理解.

#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=105;
double p[N][N],w[N*10][N],f[N*10][N];
inline void dfs(int root,int l,int r,int val){
    if(l==r){//叶子节点,即最后一轮比赛
        w[root][l]=1;
        f[root][l]=val;
        return;
    }
    int mid=(l+r)>>1;
    dfs(root<<1,l,mid,val/2);
    dfs(root<<1|1,mid+1,r,val/2);
    for(int i=l;i<=mid;++i)
        for(int j=mid+1;j<=r;++j){
            w[root][i]+=w[root<<1][i]*w[root<<1|1][j]*p[i][j];
        }
    for(int i=mid+1;i<=r;++i)
        for(int j=l;j<=mid;++j){
            w[root][i]+=w[root<<1][j]*w[root<<1|1][i]*p[i][j];
        }
    for(int i=l;i<=mid;++i)
        for(int j=mid+1;j<=r;++j){
            f[root][i]=max(f[root][i],w[root][i]*val+f[root<<1][i]+f[root<<1|1][j]);
        }
    for(int i=mid+1;i<=r;++i)
        for(int j=l;j<=mid;++j){
            f[root][i]=max(f[root][i],w[root][i]*val+f[root<<1][j]+f[root<<1|1][i]);
        }
}
int main(){
    int n=read();n=1<

你可能感兴趣的:(CF859D Third Month Insanity)