• [清华集训2017]榕树之心[树dp]


    题意

    题目链接

    分析

    • 首先解决 (subtask3) ,我们的策略就是进入子树,然后用其它子树来抵消,注意在子树内还可以抵消。

    • 可以转化为此模型:有一个数列 (a) ,每次我们可以选定两个值 (>0) 的数并让他们同时减 1,让最后 (S=sum a) 最小。

      • 如果最大的数 (a_{mx}ge frac{S}{2}) ,显然答案为 (2*a_{mx}-S)

      • 否则我们每次把最大和次大的两个数进行操作,容易证明最后的答案为 (S m mod 2)

    • 现在数列的每一项就是子树的大小。

    • 也就是说,如果我们能够尽量多地让重儿子内部相互抵消(假设最多抵消 (x) 个节点,那么可以保留的节点就是 ((x+2k+1))),以至于重儿子剩余的大小 (le frac{S}{2}) 且保持最大或最大-1(如果只有两棵子树,且初始次大子树比最大子树小1不会影响讨论),就可以让结果变得至多为 1 了。

    • 发现这变成了一个子问题,所以记状态 (f_u) 表示在 (u) 进行一系列抵消后最少剩下多少节点。转移就比较显然了。

    • 对于不止查询一号点的情况,给每个点记个最大和次大的儿子, dfs 到 (u) 时把路径上的点看成一个点即可。

    • 时间复杂度 (O(n)​)

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    #define go(u) for(int i = head[u], v = e[i].to; i; i=e[i].lst, v=e[i].to)
    #define rep(i, a, b) for(int i = a; i <= b; ++i)
    #define pb push_back
    #define re(x) memset(x, 0, sizeof x)
    inline int gi() {
        int x = 0,f = 1;
        char ch = getchar();
        while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar();}
        while(isdigit(ch)) { x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
        return x * f;
    }
    template <typename T> inline bool Max(T &a, T b){return a < b ? a = b, 1 : 0;}
    template <typename T> inline bool Min(T &a, T b){return a > b ? a = b, 1 : 0;}
    const int N = 1e5 + 7, inf = 0x3f3f3f3f;
    int n, W, T;
    int ans[N], dep[N];
    vector<int>G[N];
    struct data {
    	int son, f;
    	data(){}data(int son, int f):son(son), f(f){}
    	bool operator <(const data &rhs) const {
    		if(son != rhs.son) return son < rhs.son;
    		return f > rhs.f;
    	}
    	bool operator ==(const data &rhs) const {
    		return son == rhs.son && f == rhs.f;
    	}
    }f[N][2], g[N];
    void Add(int a, int b) {
    	G[a].pb(b);
    	G[b].pb(a);
    }
    void dfs1(int u, int fa) {
    	g[u].son = 1;
    	for(auto v:G[u]) if(v ^ fa) {
    		dep[v] = dep[u] + 1;
    		dfs1(v, u);
    		if(f[u][0] < g[v]) {
    			f[u][1] = f[u][0];
    			f[u][0] = g[v];
    		}else if(f[u][1] < g[v]) {
    			f[u][1] = g[v];
    		}
    		g[u].son += g[v].son;
    	}
    	if(f[u][0].son) {
    		if(g[u].son - f[u][0].son - 1 >= f[u][0].f + 1)
    			g[u].f = (g[u].son - 1) % 2;
    		else
    			g[u].f = f[u][0].f + 1 - (g[u].son - f[u][0].son - 1);
    	}else g[u].f = 0;
    }
    void dfs2(int u, int fa, data mx) {
    	data now = max(mx, f[u][0]);
    	int son = n - dep[u];
    	if(now.son) {
    		if(son - now.son - 1 >= now.f + 1)
    			ans[u] = (son - 1) % 2;
    		else
    			ans[u] = now.f + 1 - (son - now.son - 1);
    	}
    	for(auto v:G[u]) if(v ^ fa) {
    		if(f[u][0] == g[v]) dfs2(v, u, max(mx, f[u][1]));
    		else dfs2(v, u, max(mx, f[u][0]));
    	}
    }
    int main() {
    	W = gi(), T = gi();
    	while(T--) {
    		n = gi();
    		rep(i, 1, n) G[i].clear(), f[i][0] = f[i][1] = g[i] = data(0, inf);
    		rep(i, 1, n - 1) {
    			int a = gi(), b = gi();
    			Add(a, b);
    		}
    		dfs1(1, 0);
    		dfs2(1, 0, data(0, inf));
    		if(W == 3)
    			printf("%d
    ", ans[1] == 0);
    		else
    			rep(i, 1, n) printf("%d", ans[i] == 0);
    		puts("");
    	}
    	return 0;
    }
    
  • 相关阅读:
    Excel操作基本方法 服务器端不用安装Office工具
    使用IrisSkin2给软件"换肤"
    手机进行GPRS连接的代码(C#.NET开发)
    C# DataGridView 倒出word
    win2003优化大全 (转载)
    c# Invoke和BeginInvoke 区别
    关于sqlite3使用top的一些规则总结
    C#拷贝文件和复制文件夹实例代码 C#拷贝文件
    c# FileSystemWatcherc# FileSystemWatcher控件的使用方法控件的使用方法
    所见即所得富文本编辑器实现原理 转
  • 原文地址:https://www.cnblogs.com/yqgAKIOI/p/10533441.html
Copyright © 2020-2023  润新知