codeforce708C:树形dp+二次扫描

题目链接


题目大意:
给你一棵树,问你对于每一个点是否可以在树上删掉一条边,再增加一条边,使它成为树的重心。
tips:一个点成为重心的条件为它的每个子树大小不超过n/2。


解题思路:首先树上问题一般都要转有根树
1.对于一个点它有两个方向:就是向它儿子,就是向它父亲
2.要实现树形dp我们要找到儿子和父亲的关系
3.首先我们发现如果一个节点要是边连出去的子树最多只有一个大于 n / 2 n/2 n/2
4.如果这个子树的大于 n / 2 n/2 n/2那么我们就要对这个子树进行操作
5.首先我们要从子树挑除一个最大的并且不超过 n / 2 n/2 n/2的子树[子树的子树]直接连接到目标点,并且剩下的不能超过 n / 2 n/2 n/2


通过上面的分析:我们知道我们对于每颗子树要求什么?:
1.首先我们要求出每个子树的大小判断是否大于 n / 2 n/2 n/2
2.我们要统计子树里面小于n/2的最大的子树是多大
3.设一个down[u]是u节点向下子树中的最大值,up[u]是向上的最大值,在第二次扫描的时候如果父亲方向的siz<n/2的话我们就要更新up[u]
4.其实这里我们可以用一个set去维护每次取最大值


代码:


#include <iostream>
#include <cstdio>
#include <stack>
#include <sstream>
#include <limits.h>
#include <vector>
#include <map>
#include <cstring>
#include <deque>
#include <cmath>
#include <iomanip>
#include <unordered_map>
#include <queue>
#include <algorithm>
#include <set>
#define mid ((l + r) >> 1) 
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define _for(i,a,b) for( int i = (a); i < (b); ++i)
#define _rep(i,a,b) for( int i = (a); i <= (b); ++i)
#define for_(i,a,b) for( int i = (a); i >= (b); -- i)
#define rep_(i,a,b) for( int i = (a); i > (b); -- i)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define hash Hash
#define next Next
#define pb push_back
#define f first
#define s second
#define y1 Y
using namespace std;
const int N = 1e7 + 10, MOD = 1e9 + 7;
const int maxn = 4e5 + 10;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) 
{
    read(first);
    read(args...);
}
vector<int>G[maxn << 1];
int n;
int maxsiz[maxn << 1];//统计最大的子树在筛选的时候要去除大于n/2的子树 
int siz[maxn << 1];//统计siz 
int down[maxn << 1], up[maxn << 1];
inline void dfs(int u, int fa)
{
	siz[u] = 1; 
	for(int i = 0; i < G[u].size(); ++ i)
	{
		int v = G[u][i];
		if(v == fa) continue;
		dfs(v,u);
		siz[u] += siz[v];
	    down[u] = max(down[u],(siz[v]>n/2)?down[v]:siz[v]);
	    maxsiz[u] = max(siz[v],maxsiz[u]);
	}
	return;
}

inline void dfs1(int u, int fa)
{
	multiset<int> s;
	for(auto it : G[u])
	  if(it != fa)
	  s.insert((siz[it]>n/2)?down[it]:siz[it]);
	for(auto it :G[u])
	{
		if(it == fa) continue;
		if(n-siz[it]<=n/2) up[it] = max(up[it],n-siz[it]);
		else 
		{
			up[it] = max(up[it],up[u]);
			s.erase(s.find((siz[it]>n/2)?down[it]:siz[it]));
			if(!s.empty()) up[it] = max(up[it],*s.rbegin());
			s.insert((siz[it]>n/2)?down[it]:siz[it]);
		}
		dfs1(it,u);
	} 
}

int main()
{
    read(n);
    for(int i = 1; i < n; ++ i)
    {
    	int l, r;
    	read(l,r);
    	G[l].pb(r);
    	G[r].pb(l);
	}
	dfs(1,0);
    dfs1(1,0);
	for(int i = 1; i <=n; ++i)
	{
		int ans = 1;
	    if(maxsiz[i] > n / 2) ans = maxsiz[i] - down[i] <= n / 2;
	    if(n - siz[i] > n / 2) ans = n - siz[i] - up[i] <= n / 2;
	    cout << ans <<" ";
	}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值