• AT2293[AGC009D] Uninity【贪心,状压】


    正题

    题目链接:https://www.luogu.com.cn/problem/AT2293


    题目大意

    给出一棵树,求它一棵点分树的最小深度。

    \(1\leq n\leq 10^5\)


    解题思路

    点分树的做法是直接找重心,但是两个重心我们很难确定找哪个,所以这个方法行不通。

    但是这样我们大概能确定答案的上界是\(\log n\)级别的。考虑我们记每个点的点分子树深度\(d_i\),那么\(d_i\)肯定满足对于一对深度相同的\((x,y)\),它们的路径上肯定存在一个点\(z\)满足\(d_z>d_x,d_z>d_y\)

    那么同样的,如果我们得到一个满足这个条件的数组\(d\),我们也能构造出一棵合法的点分树,所以我们的目的就是要最小化\(d\)的值。

    考虑一个构造方法,对于一个节点\(x\)的深度\(p\),首先如果它的儿子中有深度为\(p\)的点那么显然不合法,如果存在一个在它不同子树中的节点\(y\)的深度\(\geq p\),并且\(x\leftrightarrow y\)路径上节点深度都不超过\(p\),那么显然也不合法。

    显然上面这两个条件我们可以用状压搞定。

    但是这样构造为什么是合法的呢?我也不知道,只能感性证明一下。能发现我们的操作中如果一个节点\(x\)选择了深度\(p\),那么它往上的限制中所有深度\(<p\)的限制都会被打开,也就是说实际上选择更大的深度并不会放松后面的限制,所以选最小的更优。

    时间复杂度:\(O(n\log n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=1e5+10;
    struct node{
    	int to,next;
    }a[N<<1];
    int n,tot,ls[N],f[N],ans;
    void addl(int x,int y){
    	a[++tot].to=y;
    	a[tot].next=ls[x];
    	ls[x]=tot;return;
    }
    void dfs(int x,int fa){
    	int p=0,s=0;
    	for(int i=ls[x];i;i=a[i].next){
    		int y=a[i].to;
    		if(y==fa)continue;
    		dfs(y,x);
    		for(int j=30;j>=0;j--)
    			if(s&f[y]&(1<<j))
    			{p=max(p,j+1);break;}
    		s|=f[y];
    	}
    	while((s>>p)&1)p++;
    	ans=max(ans,p);
    	f[x]=s|((1<<p+1)-1);
    	f[x]=f[x]^((1<<p)-1);
    	return;
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1,x,y;i<n;i++){
    		scanf("%d%d",&x,&y);
    		addl(x,y);addl(y,x);
    	}
    	dfs(1,0);
    	printf("%d\n",ans);
    	return 0;
    }
    
  • 相关阅读:
    Spring Boot简明教程之实现Web开发及常用参数获取方式分析
    SpringBoot 简明教程之项目属性配置(三):配置文件优先级及多配置切换
    史上最简单MySQL教程详解(进阶篇)之存储过程(二)
    史上最简单MySQL教程详解(进阶篇)之存储过程(一)
    文字从中间向两边延展
    字符串转化成驼峰命名
    统计字符串字符个数
    while求和(1到100)
    for循环实现乘阶
    递归遍历所有ul下的所有子节点
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/16221509.html
Copyright © 2020-2023  润新知