zoukankan      html  css  js  c++  java
  • 【NOIP2019提高组正式赛day2】树的重心(centroid)

    On contest

    考场打了(55)分暴力分(其实应该能拿(75)的,但我没打)

    Solution

    现在听了讲,发现这道题其实也并非那么难的。
    首先我们要知道几个性质:
    1.重心最多只有两个。
    2.重心一定是从根跳重儿子一直跳到某个位置。
    对于1显然。
    对于2的话,假设重心是一个轻儿子(x),那么(siz[x])一定小于(siz[son[fa[x]]]),再加上(fa[x])及以上的点肯定大于整个树的节点数的一半,那么(x)一定不是重心了。

    所以求一个数的重心,我们可以从根一直倍增在满足条件的情况跳,那么跳到的最终点(x)一定是重心,而(fa[x])则可能是重心。

    所以我们可以枚举要删的边。
    我们发现,对于删的边的下面求重心,没有影响,就跳重儿子就可以了。
    但对于删的边的上面,那就有点问题了。
    我们发现,上面树的重边可能会发生改变(因为下面删了一个子树)。
    所以我们要先强制上面的不跳到下面去,然后再看看跳次重儿子(也就是可能变成重儿子的点)是否能满足条件。

    如此即可。

    Code

    #include <cstdio>
    #include <cstring>
    #define N 300010
    #define ll long long
    #define mem(x, a) memset(x, a, sizeof x)
    #define fo(x, a, b) for (int x = (a); x <= (b); x++)
    #define fd(x, a, b) for (int x = (a); x >= (b); x--)
    using namespace std;
    struct node{int v, fr;}e[N << 1];
    int T, n, tail[N], fa[N], a[N][2], cnt = 0, number = 0;
    int siz[N], dep[N], dfn[N], son[N][21], cson[N];
    ll ans = 0;
    
    inline int read()
    {
    	int x = 0; char c = getchar();
    	while (c < '0' || c > '9') c = getchar();
    	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x;
    }
    
    inline void add(int u, int v) {e[++cnt] = (node){v, tail[u]}; tail[u] = cnt;}
    
    void dfs(int x)
    {
    	dfn[x] = ++number;
    	siz[x] = 1, son[x][0] = cson[x] = 0;
    	for (int p = tail[x], v; p; p = e[p].fr)
    	{
    		v = e[p].v;
    		if (v == fa[x]) continue;
    		dep[v] = dep[x] + 1, fa[v] = x;
    		dfs(v), siz[x] += siz[v];
    		if (siz[v] > siz[son[x][0]])
    			cson[x] = son[x][0], son[x][0] = v;
    		else if (siz[v] > siz[cson[x]]) cson[x] = v;
    	}
    	for (int i = 0; i <= 18; i++)
    		son[x][i + 1] = son[son[x][i]][i];
    }
    
    void easy_query(int x, int tot)
    {
    	for (int i = 18; i >= 0; i--)
    		if (son[x][i] && siz[son[x][i]] >= tot - siz[son[x][i]]) x = son[x][i];
    	ans += x;
    	if (siz[x] * 2 == tot) ans += fa[x];
    }
    
    int got(int x, int cnd) {return siz[x] - (dfn[cnd] >= dfn[x] && dfn[cnd] <= dfn[x] + siz[x] - 1 ? siz[cnd] : 0);}
    
    void medium_query(int x, int tot, int cnd)
    {
    	int size;
    	for (int i = 18; i >= 0; i--)
    	{
    		size = got(son[x][i], cnd);
    		if (! son[x][i] || (dfn[son[x][i]] >= dfn[cnd] && dfn[son[x][i]] <= dfn[cnd] + siz[cnd] - 1)) continue;
    		if (size >= tot - size) x = son[x][i];
    	}
    	size = siz[cson[x]];
    	if (size >= tot - size)
    	{
    		x = cson[x];
    		for (int i = 18; i >= 0; i--)
    		{
    			size = siz[son[x][i]];
    			if (size >= tot - size) x = son[x][i];
    		}
    	}
    	ans += x;
    	size = got(x, cnd);
    	if (size * 2 == tot) ans += fa[x];
    }
    
    void solve(int x)
    {
    	if (x != 1) easy_query(x, siz[x]);
    //	solve son
    	if (son[x][0])
    	{
    		medium_query(1, siz[1] - siz[son[x][0]], son[x][0]); 
    		solve(son[x][0]);
    	}
    	for (int p = tail[x], v; p; p = e[p].fr)
    	{
    		v = e[p].v;
    		if (v == fa[x] || v == son[x][0]) continue;
    		medium_query(1, siz[1] - siz[v], v);
    		solve(v);
    	}
    }
    
    int main()
    {
    	freopen("centroid.in", "r", stdin);
    	freopen("centroid.out", "w", stdout);
    	T = read();
    	while (T--)
    	{
    		mem(tail, 0); cnt = 0;
    		ans = 0;
    		n = read();
    		fo(i, 1, n - 1)
    		{
    			a[i][0] = read(), a[i][1] = read();
    			add(a[i][1], a[i][0]), add(a[i][0], a[i][1]);
    		}
    		dfs(1);
    		solve(1);
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    
    转载需注明出处。
  • 相关阅读:
    Docker+Jenkins持续集成环境(1)使用Docker搭建Jenkins+Docker持续集成环境
    DockOne技术分享(二十):Docker三剑客之Swarm介绍
    最佳实战Docker持续集成图文详解
    Spring Cloud Netflix Eureka源码导读与原理分析
    JVM内存区域的划分(内存结构或者内存模型)
    深入理解JVM(一)——JVM内存模型
    java多线程有哪些实际的应用场景?
    深入理解Java类加载器(1):Java类加载原理解析
    【深入Java虚拟机】之四:类加载机制
    OAuth 2和JWT
  • 原文地址:https://www.cnblogs.com/jz929/p/11918540.html
Copyright © 2011-2022 走看看