[POI2013]LUKTriumphal arch

考虑二分答案。
肯定是对每个节点的儿子都要染色。
当时以为是所有节点的儿子数量的最多的。
后来发现前面如果有多余可以多给后面的。

\(f[i]\)\(i\)节点及子树的和标准操作的差。

那么\(f[i] = \sum_{(i \to v)}\ max(0,f[v]) + son[i] - k\)

考虑\(f[1]\)是否下于0即可。

#include<iostream>
#include<cstdio>
#define ll long long 
#define N 300005

ll n;

struct P{
	int to,next;
}e[N << 1];

ll cnt,head[N];

inline void add(int x,int y){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	head[x] = cnt;
}

ll son[N];

inline void dfs(int u,int f){
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == f)continue;
		son[u] ++ ;
		dfs(v,u);
	}
}

ll f[N];
ll k;

inline void dfn(int u,int fa){
	f[u] = son[u] - k;
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == fa)continue;
		dfn(v,u);
		f[u] += std::max((ll)0,f[v]);
	}
}

inline bool check(ll x){
	k = x;
	for(int i = 1;i <= n;++i)
	f[i] = 0;
	dfn(1,0);
	return (f[1] <= 0);
}

#define mid ((l + r) >> 1)

int main(){
	scanf("%lld",&n);
	for(int i = 1;i < n;++i){
		ll x,y;
		scanf("%lld%lld",&x,&y);
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	ll l = 0,r = n;
	while(l + 1 < r){
		if(check(mid))
		r = mid;
		else
		l = mid;
	}
	r ++ ;
	while(check(r - 1))
	r = r - 1;
	std::cout<<r<<std::endl;
}
原文地址:https://www.cnblogs.com/dixiao/p/15092804.html