zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    随笔:自我介绍与软工 5 问
    作业要求 20200924-1 每周例行报告
    作业要求20200924-2 功能测试
    作业要求 20200924-5 四则运算试题生成,结对
    作业要求20200924-3 单元测试,结对
    作业要求20200924-4 代码规范,结对要求
    20200929-git地址
    作业要求 20200917-3 白名单
    作业要求 20200917-1 每周例行报告
    20200917-2 词频统计
  • 原文地址:https://www.cnblogs.com/Mohogany/p/13908707.html
Copyright © 2011-2022 走看看