GMOJ 6807. 【2020.10.29提高组模拟】tree

GMOJ 6807. 【2020.10.29提高组模拟】tree

题目描述

有一个 n 个节点的树,编号分别是 1 到 n,每个节点上有一个颜色,一共有 m 种颜色,保证每种颜色至少出现 1 次。
你需要选择一个点作为根,同时找一个树上节点的非空子集 T,满足每种颜色都至少在T 中出现一次,并且 T 中所有点的 LCA 的深度最大。定义你选的根深度为 1,儿子的深度是父亲深度+1。

题解

这道题考试时我只拿了75分,打的是线段树合并。

做法大致是一样的,枚举以哪个点作为(lca) ,然后分两种情况讨论:

  1. 点集T是当前点(x) 的子树,那么我们只需要找到(x) 上方离他最远的点就行了。
  2. 点集T是除(x) 外的子树,那么我们只需要找到(x) 下方离他最远的点就可以了。

现在的问题就只剩下如何判断(x) 的子树及外子树是否包含全部颜色就行了。

  1. 对于(x) 的子树,将同种颜色的点按(dfn) 序排序,然后将每个点到根的路径上标记加一,然后再减去相邻两个点的(lca) 到根的标记避免重复。可以用差分计算。
  2. 对于(x) 的子树外的点,可以找到同种颜色所有点的(lca) ,容易发现,从这个(lca) 到根上的路径上的所有点,他们的子树之外都是不包含所有点的。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000010 
#define M 100010
#define K 20
using namespace std;
int n,m,col[N],tot,x,y,i,q[N],fa[N][K+1],dep[N],last[M],dfn[N],num,bz1[N],bz2[N],f[N],f1[N],g[N],clca[M],ans;
struct edge{
	int to,next;
}e[N*2];
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
void insert(int x,int y){
	tot++;
	e[tot].to=y;
	e[tot].next=q[x];
	q[x]=tot;
}
void dfs_fa(int x,int father){
	int i,y;
	fa[x][0]=father;
	dep[x]=dep[father]+1;
	num++;
	dfn[num]=x;
	f[x]=1;
	for (i=1;i<=K;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
	for (i=q[x];i;i=e[i].next){
		y=e[i].to;
		if (y!=father){
			dfs_fa(y,x);
			if (f[y]+1>f[x]) f1[x]=f[x],f[x]=f[y]+1;
			else f1[x]=max(f1[x],f[y]+1);
		}
	}
}
void dfs_bz(int x,int father){
	int i,y;
	g[x]=g[father]+1;
	if (f[x]+1==f[father]) g[x]=max(g[x],f1[father]+1);
	else g[x]=max(g[x],f[father]+1);
	for (i=q[x];i;i=e[i].next){
		y=e[i].to;
		if (y!=father){
			dfs_bz(y,x);
			bz1[x]+=bz1[y];
			if (bz2[y]) bz2[x]=1;
		}
	}
}
int lca(int x,int y){
	int i;
	if (dep[x]<dep[y]) swap(x,y);
	for (i=K;i>=0;i--)
		if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	if (x==y) return x;
	for (i=K;i>=0;i--)
		if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=read();m=read();
	for (i=1;i<=n;i++) col[i]=read();
	for (i=1;i<n;i++){
		x=read();y=read();
		insert(x,y);
		insert(y,x);
	}
	dfs_fa(1,0);
	for (i=1;i<=n;i++){
		bz1[dfn[i]]++;
		if (last[col[dfn[i]]])
			bz1[lca(last[col[dfn[i]]],dfn[i])]--;
		if (!clca[col[dfn[i]]]) clca[col[dfn[i]]]=dfn[i];
		else clca[col[dfn[i]]]=lca(dfn[i],clca[col[dfn[i]]]);
		last[col[dfn[i]]]=dfn[i];
	}
	for (i=1;i<=m;i++)
		bz2[clca[i]]=1;
	dfs_bz(1,0);
	for (i=1;i<=n;i++){
		if (bz1[i]==m) ans=max(ans,g[i]);
		if (!bz2[i]) ans=max(ans,f[i]+1);
	}
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/Mohogany/p/13908707.html