Codeforces 538E. Demiurges Play Again (树形DP)

题目描述

传送门

题目大意:给出一个有根树,A,B两个人轮流操作。
给每个叶子节点分配权值[1..叶子节点数]的一个排列。每次每个人可以选择任意一条边走,A希望最后取得的叶子节点的权值尽可能大,B希望最后取得的叶子节点的权值尽可能的小,两个人都绝对聪明,问如果让A分配权值最后能得到的最大权值是多少,如果让B分配权值最后能得到的最小权值是多少。

题解

已A的操作为例。
f[i]表示到达节点i能获得其子树中第几大的权值。显然A可以控制奇数层的节点,B可以控制偶数层的节点。
轮到A操作的时候,他一定会选择 min(f[son]) ,安排权值的时候把所有最大的都放置到一棵子树上。
轮到B操作的时候,他会尽可能选择小的,所以A的目标就是使最小值最大,那么最好的方式就是平均分配。
最后B只能得到 f[son]
B的操作也是一样的,不过用f[i]表示第几小即可。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 400003
using namespace std;
int n,tot,nxt[N],v[N],point[N],f[N],deep[N],leaf;
void add(int x,int y)
{
    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs_A(int x,int fa)
{
    deep[x]=deep[fa]+1;
    bool pd=false;
    if (deep[x]&1) {
        f[x]=n;
        for (int i=point[x];i;i=nxt[i]) {
            if (v[i]==fa) continue;
            dfs_A(v[i],x);
            f[x]=min(f[x],f[v[i]]);
            pd=true;
        }
    }
    else {
        for (int i=point[x];i;i=nxt[i]){
            if (v[i]==fa) continue;
            dfs_A(v[i],x);
            f[x]=f[x]+f[v[i]];
            pd=true;
        }
    }
    if (!pd) f[x]=1,leaf++;
}
void dfs_B(int x,int fa)
{
    bool pd=false;
    if (!(deep[x]&1)){
        f[x]=n;
        for (int i=point[x];i;i=nxt[i]) {
            if (v[i]==fa) continue;
            dfs_B(v[i],x);
            f[x]=min(f[x],f[v[i]]);
            pd=true;
        }
    }
    else {
        f[x]=0;
        for (int i=point[x];i;i=nxt[i]){
            if (v[i]==fa) continue;
            dfs_B(v[i],x);
            f[x]=f[x]+f[v[i]];
            pd=true;
        }
    }
    if (!pd) f[x]=1;
}
int main()
{
    freopen("a.in","r",stdin);
    scanf("%d",&n);
    for (int i=1;i<n;i++) {
        int x,y; scanf("%d%d",&x,&y);
        add(x,y);
    }
    dfs_A(1,0); 
    printf("%d ",leaf-f[1]+1);
    dfs_B(1,0);
    printf("%d\n",f[1]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值