[联合集训6-19] 山洞 点分树

7 篇文章 0 订阅
4 篇文章 0 订阅

一句话题意就是求点分树最小深度。
点分树有一个性质:我们称点 i i 在点分树上距叶子的距离为其权值wi,那么对于两个点 u,v u , v 满足 wu=wv=k w u = w v = k ,在原树路径 (u,v) ( u , v ) 上一定存在点 t t 使得wt>k,证明很显然。
我们对每个点 i i 求出一个二进制状态,二进制第k位表示该点子树中存不存在一个 wj=k w j = k 的点 j j ,且(i,j)路径上没有 w>k w > k 的点,因为点分树的深度是 O(log) O ( log ) 的,这个二进制状态不会很大。假设我们已知 i i 所有儿子的二进制状态,那么wi的取值就受到两个限制:
1. 如果存在两个儿子第 k k 位同为1,那么 wi>k w i > k
2. 如果存在某个儿子第 k k 位为1,那么 wik w i ≠ k
在满足这两个性质的前提下, wi w i 取合法的最小值(至于为什么取最小的最优不太会证。。。但感觉挺显然的),之后 i i 点的二进制状态就是其所有儿子状态或和,再把wi位赋成 1 1 并把wi1一下的位赋为 0 0 <script type="math/tex" id="MathJax-Element-28">0</script>即可。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100010
using namespace std;
int n,tote,to[N<<1],nxt[N<<1],con[N],w[N],ans;
void ins(int x,int y)
{
    to[++tote]=y;
    nxt[tote]=con[x];
    con[x]=tote;
}
void dfs(int v,int fa)
{
    int dep=-1;
    bool flag=0;
    for(int p=con[v];p;p=nxt[p])
        if(to[p]!=fa)
        {
            dfs(to[p],v);
            int tmp=w[v]&w[to[p]];
            for(int i=30;i>=0;i--)
                if((tmp>>i)&1) {dep=max(dep,i);break;}
            w[v]=w[v]|w[to[p]];
            flag=1;
        }   
    if(!flag) {w[v]=1;return ;} 
    for(int i=dep+1;i<=30;i++)
        if((w[v]>>i)&1) 
            dep=max(dep,i);
        else break;
    ans=max(ans,dep+1);
    int R=(1<<(dep+1)),T=((1<<30)-1)^(R-1);
    w[v]=(w[v]&T)|R;    
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        ins(x,y);ins(y,x);
    }
    dfs(1,0);
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值