uoj problem 11 ydc的大树

题目大意:

给定一颗黑白树.允许删除一个白点.最大化删点后无法与删点前距自己最远的黑点连通的黑点个数.并求出方案数.

题解:

这道题很棒棒啊.

一开始想了一个做法,要用LCT去搞,特别麻烦而且还是(O(nlogn))

% 了vfk的题解才知道这道题居然是(O(n))的...


我们有一个结论:一个点到其最远点的路径一定经过树的直径的重心.

因为所有直径的重心一定相同,所以我们知道重心最多有两个,并且一定连续.

所以我们将重心提至根,以新的根重新建树.那么我们现在就可以发现我们要切断的所有路径一定都经过根.

所以我们考虑枚举删除的白点.其子树当中的所有黑点所连出的路径一定都被切断

那么我们现在考虑计算除这些子树外的路径.

对于同处于根的同一个儿子中的其他的黑点一定无法被切断.

这时我们要讨论一下我们当前的枚举点是否处于到根路径前2大的黑点所在的路径.

如果是,那么我们仍需要讨论一下这个路径是否全由这个以这个点为根的子树更新来.

然后再瞎XX判一些东西即可.(不具体说是什么是因为说不清楚...还不懂的话自己YY吧)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 100010;
struct Edge{
    int to,next,dis;
}G[maxn<<1];
int head[maxn],cnt,rt;
void add(int u,int v,int d){
    G[++cnt].to = v;
    G[cnt].next = head[u];
    head[u] = cnt;
    G[cnt].dis = d;
}
bool col[maxn];
int f[maxn],g[maxn],pos;
#define v G[i].to
void dfs(int u,int f,int *dis){
    if(col[u] && dis[pos] < dis[u]) pos = u;
    for(int i = head[u];i;i=G[i].next){
	if(v == f) continue;
	dis[v] = dis[u] + G[i].dis;
	dfs(v,u,dis);
    }
}
int belong[maxn],siz[maxn];
void dfs2(int u,int fa,int d){
    if(fa == rt) belong[u] = u;
    else belong[u] = belong[fa];
    siz[u] = col[u];
    for(int i = head[u];i;i=G[i].next){
	if(v == fa) continue;
	dfs2(v,u,d+G[i].dis);
	siz[u] += siz[v];
    }
    if(siz[u]){
	f[u] = d;g[u] = 1;
	for(int i = head[u];i;i=G[i].next){
	    if(v == fa) continue;
	    if(f[v] > f[u]){
		f[u] = f[v];
		g[u] = g[v];
	    }else if(f[v] == f[u]) g[u] += g[v];
	}
    }
}
#undef v
int main(){
    int n,m;read(n);read(m);
    for(int i=1,x;i<=m;++i){
	read(x);col[x] = true;
    }
    for(int i=1,u,v,d;i<n;++i){
	read(u);read(v);read(d);
	add(u,v,d);add(v,u,d);
    }
    pos = 0;dfs(1,1,f);
    int x = pos;pos = 0;
    dfs(x,x,g);

    memset(f,0,sizeof f);
    dfs(pos,pos,f);
    rt = 0;
    for(int i=1;i<=n;++i){
	if(f[i] + g[i] == f[x]){
	    if(rt == 0) rt = i;
	    else if(abs(f[i] - g[i]) < abs(f[rt] - g[rt])) rt = i;
	}
    }
    memset(f,0,sizeof f);
    memset(g,0,sizeof g);
    dfs2(rt,rt,0);
    int mx = 0,cmx = 0,mxid = 0,cmxid = 0;
    for(int i = head[rt];i;i=G[i].next){
	int v = G[i].to;
	if(siz[v] == 0) continue;
	if(f[v] > mx){
	    cmx = mx;cmxid = mxid;
	    mx = f[v];mxid = v;
	}else if(f[v] == mx){
	    if(mx == cmx){
		mxid = cmxid = 0;
		break;
	    }else{
		cmx = mx;cmxid = mxid;
		mx = f[v];mxid = v;
	    }
	}else if(f[v] > cmx){
	    cmx = f[v];
	    cmxid = v;
	}
    }
    int ans = 0,ans_cnt = 0;
    if(col[rt] == false) ans = m,ans_cnt = 1;
    for(int i = 1;i<=n;++i){
	if(i == rt || col[i]) continue;
	int res = siz[i];
	if(siz[i]&&(f[i] == f[belong[i]])&&(g[i] == g[belong[i]])){
	    if(belong[i] == mxid){
		if(f[mxid] > f[cmxid]) res += m - siz[mxid];
		else res += siz[cmxid];
	    }else if(belong[i] == cmxid) res += siz[mxid];
	}
	if(res > ans) ans = res,ans_cnt = 1;
	else if(res == ans) ++ ans_cnt;
    }
    printf("%d %d
",ans,ans_cnt);
    return 0;
}


原文地址:https://www.cnblogs.com/Skyminer/p/6636850.html