2018.8.12T1(树形dp)

描述
最近在训练的时候,可怜看到了这样的一个题:

给出一颗 n 个点的有根树,最开始有一个棋子放在根节点。两个人交替移动棋子:如果当前棋子处在一个叶子节点上,那么游戏立刻结束;否则当前玩家需要选择当前节点的一个孩子,然后把棋子移动到这个孩子上。

游戏的得分是棋子最终所在节点的深度(根节点深度为 1)。先手想要最大化这个得分,后手想要最小化这个得分。如果两个人都以最优策略行动,那么最终游戏的得分是多少?

这个题目很简单,于是可怜简单修改了一下这个题目。

现在给出 n 个节点的有根树 T,根节点为 1 号节点。你可以对这棵树进行若干次操作,每一次操作你可以选择树上的一个叶子节点 i,然后在树上加入一个新的节点作为 i 的儿子。(在这次插入后 i 的下方不能再插入节点,因为它已经不是叶子了,但是你可以在新节点的下方插入节点)。

再给出整数 K,令 f(K) 的值等于:为了让最优游戏的得分恰好为 K,你至少需要进行多少次操作。如果无论怎么进行操作都没法让得分恰好为 K,那么 f(K)=−1

现在可怜让你计算的是 limK+(f(K+1)f(K)) l i m K → + ∞ ( f ( K + 1 ) − f ( K ) )

如果你对这个极限感到自闭,这儿给出一个简单的小提示,不保证一定和标算有关,但是保证能让你不那么自闭:

如果这个极限存在,那么一定存在一个 N>0,满足对于任意 K>N,2f(K)=f(K−1)+f(K+1),且 f(K)−f(K−1) 就是极限的值。
如果这个极限不存在,那么对于任意 N>0N>0,都存在 K>N 满足 2f(K)≠f(K−1)+f(K+1)
输入格式
第一行一个整数 n 表示节点的个数。

接下来 n−1 行每一行两个整数 u,v(1≤u≠v≤n)) 表示树上的一条边。

输出格式
输出一行一个整数表示答案,如果极限不存在则输出 −1−1。

样例1
样例输入1
8
1 2
2 3
2 4
4 5
4 8
5 6
5 7
样例输出1
2
限制与约定
本题采用捆绑测试,共有 3个子任务:

n≤50,30 分。
n≤200,30 分。
n≤ 105 10 5 ,100分。
时间限制:1s

空间限制:512MB


对每个点,我们存储先手在这个点能到达的最深的深度 mx m x 和后手在这个点能到达的最小的深度 mn m n
mn m n 由下一层的 mx m x 转移, mx m x 由下一层的 mn m n 转移
然后为了使最后深度尽可能大,在所有后手的点的子树先手可能到达的叶子结点我们都要使其深度随着k增大增大
也就是我们要在先手的点取下一层 son s o n min m i n
在后手的点取下一层 son s o n 的和
满足叶子结点个数就是答案
根本不可能存在-1的

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
const int INF =168430090;
int linkk[101000] , n , t;
int son[101000] , mn[101000] , mx[101000] , dep[101000];
struct node{
    int n , y;
}e[201000];
int read()
{
    int sum = 0;char c = getchar();bool flag = true;
    while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
    while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
    if(flag)  return sum;
     else return -sum;
}
void insert(int x,int y)
{
    e[++t].y = y;e[t].n = linkk[x];linkk[x] = t;
    e[++t].y = x;e[t].n = linkk[y];linkk[y] = t;
    return;
}  
void dfs(int x,int fa)
{
    int k = linkk[x];
    if(e[k].n == 0 && x != 1)
    {
        son[x] = 1;
        return;
    }
    if(dep[x] % 2 == 0)
    {
        son[x] = 0;
        for(int i = linkk[x];i;i = e[i].n)
            if(e[i].y != fa)
            {
                dfs(e[i].y,x);
                son[x] += son[e[i].y];
            }
    }
    else
    {
        son[x] = 0;
        for(int i = linkk[x];i;i = e[i].n)
            if(e[i].y != fa)
            {
                dfs(e[i].y,x);
                if(son[x] == 0) son[x] = son[e[i].y];
                else son[x] = min(son[x] , son[e[i].y]);
            }
    }
    return;
}
void dfs_pre(int x,int fa)
{
    int k = linkk[x];
    dep[x] = dep[fa] + 1;
    if(e[k].n == 0 && x != 1)
    {
        son[x] = 1;
        mx[x] = dep[x];
        mn[x] = dep[x];
        return;
    }
    for(int i = linkk[x];i;i = e[i].n)
        if(e[i].y != fa) 
        {
            dfs_pre(e[i].y,x);
            son[x] += son[e[i].y];
            mx[x] = max(mx[x] , mn[e[i].y]);
            if(dep[x]%2==0)mn[x] = min(mn[x] , mx[e[i].y]);
        }
    if(dep[x] % 2 == 1) mn[x] = mx[x];
    return; 
}
void init()
{
    n = read();
    rep(i,1,n-1)
    {
        int x = read() , y = read();
        insert(x,y);
    }
}
int main()
{
    init();
    memset(mn,10,sizeof(mn));
    dfs_pre(1,0);
    dfs(1,0);
    printf("%d\n",son[1]);
    return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值