zoukankan      html  css  js  c++  java
  • [Luogu3554] Poi2013 Triumphal arch

    Description

    Foreseeable和拿破仑的御用建筑师让·夏格伦在玩游戏

    		
    让·夏格伦会玩一个叫“凯旋门”的游戏:现在有一棵n个节点的树,表示一个国家 1号点代表这个国家的首都 这个游戏由两个人一起玩 一个玩家扮演视察国家的国王,另一个扮演建立凯旋门的建筑师 一开始只有首都有凯旋门 国王每次会从当前所在城市移动到一个相 邻的城市 在国王每次移动前,建筑师可以选择国家内任意不超过k个城市建造出凯旋门 如果在任意一个时刻,国王所在的城市没有凯旋门 那么国王会很生气, 扮演建筑师的玩家就输了 如果所有的城市都建立起了凯旋门而国王一直没有走到有凯旋门的城市,那么建筑师就胜利了 Foreseeable不会建筑,所以他扮演国王 而让·夏格伦则 “扮演”建筑师(他本来就是建筑师不需要扮演) 容易发现k的大小非常影响游戏有趣度…… 如果k太大,那么建筑师可以在国王行动前就在所有城市建出凯旋门,那么国王就没有胜利的希望了 ,这样Foreseeable会掀桌不玩 如果k太小,那么Foreseeable很有可能会发挥自己所剩无几的智商走到一个没有凯旋门的地方 让·夏格伦想享受虐Foreseeable的快感 所以你要帮他确 定最小的k,使得在这个游戏中,如果建筑师足够聪明的话,建筑师必胜 Input 第一行一个整数n 接下来n-1行,每行两个整数u,v,表示u,v相邻 Output 一行一个整数表示最小的k Sample Input 7 1 2 1 3 2 5 2 6 7 2 4 1 Sample Output 3 Hint 1<=n<=300000 样例解释:在foreseeable第一次行动前,让在2,3,4城市建好 凯旋门,然后接下来无论foreseeable走到哪个城市, 在5,6,7建好凯旋门就能保证让的胜利了




    答案满足单调性显然可以二分答案,但是二分完之后该如何呢?
    直接模拟显然不行...然后就被卡住了。

    看了题解。
    要跑路的人一定是从根跑到叶子节点,不会回头,因为往回走不但不会逃离,反而会让另一个人染更多的格子,因为他从根节点到这的路径一定都被染过色了。
    这为树形DP提供了基础。
    我们设 $f[i]$ 为以i为根的子树,要让B不会走到不被染色的节点的子树的最小需要被覆盖数量。
    显然 $f[x]=min left ( sum_{ysubseteq son[x]} { } f[y] + deg[x] - mid, 0 ight )$ .
    $deg[x]$ 为x节点的度数-1(因为不包括他父亲)。
    然后最后判断$f[0]$是否等于0就行了。


     
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <map>
    using namespace std;
    #define reg register 
    inline char gc() {
        static const int BS = 1 << 22;
        static unsigned char Buf[BS], *st, *ed;
        if (st == ed) ed = Buf + fread(st = Buf, 1, BS, stdin);
        return st == ed ? EOF : *st++;
    }
    #define gc getchar
    inline int read() {
        int res=0;char ch=getchar();bool fu=0;
        while(!isdigit(ch)) {if(ch=='-')fu=1;ch=getchar();}
        while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=getchar();
        return fu?-res:res;
    }
    #define N 300005
    int n;
    struct edge {
        int nxt, to;
    }ed[N*2];
    int head[N], cnt, deg[N];
    inline void add(int x, int y) {
        ed[++cnt] = (edge){head[x], y};
        head[x] = cnt;
        deg[y]++;
    }
    int f[N];
    
    int mid;
    
    void dp(int x, int fa) 
    {
        int res = 0;
        for (reg int i = head[x] ; i ; i = ed[i].nxt)
        {
            int to = ed[i].to;
            if (to == fa) continue;
            dp(to, x);
            res += f[to] + 1;
        }
        f[x] = max(res - mid, 0);
    }
    
    inline bool check()
    {
        memset(f, 0, sizeof f);
        dp(1, 0);
        return f[1] == 0;
    }
    
    int main()
    {
        n = read();
        for (reg int i = 1 ; i < n ; i ++)
        {
            int x = read(), y = read();
            add(x, y), add(y, x);
        }
        if (n == 1) return puts("0"), 0;
        int l = 1, r = n, ans;
        while(l <= r)
        {
            mid = l + r >> 1;
            if (check()) ans = mid, r = mid - 1;
            else l = mid + 1;
        }
        cout << ans << endl;
        return 0;
    }
    
    
    



  • 相关阅读:
    【C】中malloc的使用
    【C++】const成员函数的作用
    C声明和定义
    【C++】指针与引用的区别
    【C】external/internal/static/register variable and function
    【C++】Sizeof与Strlen
    【面试题目】string类实现
    【C++】public,private,protected
    【Linux】僵尸进程
    【面试题目】翻转句子中单词的顺序
  • 原文地址:https://www.cnblogs.com/BriMon/p/9697790.html
Copyright © 2011-2022 走看看