2200专项:E. Demiurges Play Again(思维 树上)

原题: http://codeforces.com/problemset/problem/538/E

题意:

一棵1为根的树,设叶子节点的个数为x,那么你现在可以将叶子编号为1~x。之后两个人开始做游戏,从根开始,两个人依次选择一个儿子,跳到儿子上,最后落在一个叶子上得分为其编号。第一个人想要得分大,第二个人小。问你怎么样排编号使之最大最小。

解析:

先来看最大的
在这里插入图片描述
看15号点,由第二个人控制。如果目标答案在15这个点产生,需要消耗3个点(第二个人会选16,17,18中小的,所以要用三个点,产生了第3大的结果)

看8号点,由第一个人控制。13和14只需要一个点即可(将13或14附为最大值,第一个人会直接选),而走15号最大只能选第3大,所以8号点的答案为1。

总结:

由第二个人控制的,对儿子的答案累加。而第一个人,在儿子中选min。最大值为第(1节点答案)大的数。


再看最小值,15号的三个儿子中放一个最小的即可,所以答案为1。8号点要想取到最小值,要放3个(第一个人无视较小的)。

所以和上面是相反的,第一个人累加,第二个人取min。此时为第(1节点答案)小的数。

#include<bits/stdc++.h>
using namespace std;
#define LL long long

const int maxn=2e5+5;
vector<int> V[maxn];
int son=0;
int dfs(int p,int fa,bool f){
    if(f){
        int res=1e9;
        for(int i=0;i<V[p].size();i++){
            int u=V[p][i];
            if(u==fa)continue;
            int val=dfs(u,p,!f);
            res=min(res,val);
        }
        if(res==1e9)return 0*(++son)+1;
        return res;
    }
    else{
        int res=0;
        for(int i=0;i<V[p].size();i++){
            int u=V[p][i];
            if(u==fa)continue;
            int val=dfs(u,p,!f);
            res+=val;
        }
        if(res==0)return 0*(++son)+1;
        return res;
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;cin>>n;
    for(int i=1;i<n;i++){
        int a,b;cin>>a>>b;
        V[a].push_back(b);
        V[b].push_back(a);
    }
    int ma=dfs(1,-1,1);
    int mi=dfs(1,-1,0);
    printf("%d %d\n",son/2-ma+1,mi);
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/90048870