【LOJ】#2330. 「清华集训 2017」榕树之心 -树形dp

本文提供了一种解决LOJ2330问题的方法,通过递归遍历树形结构来计算每棵子树中无法互相抵消的最小点数,并给出了一段C++代码实现。

传送门:loj2330


题解

先考虑根的情况( Subtask 3 S u b t a s k   3 )。
根的每个儿子及其构成的子树之间可以互相抵消。
rem[i] r e m [ i ] 表示以 i i 为根的子树最少的不能互相抵消的点数。
那么考虑根的最大儿子mx[1]
sz[1]1sz[mx[1]]rem[mx[1]] s z [ 1 ] − 1 − s z [ m x [ 1 ] ] ≤ r e m [ m x [ 1 ] ] ,大儿子可以被抵消掉,这时只需考虑 sz[1]1 s z [ 1 ] − 1 是否为偶数即可。
否则 rem[1]=rem[mx[1]]sz[1]+sz[mx[1]]+2 r e m [ 1 ] = r e m [ m x [ 1 ] ] − s z [ 1 ] + s z [ m x [ 1 ] ] + 2
其他 Subtask S u b t a s k ,把当前节点到根节点的这条链看做根,顺便记一下每个点的第二大儿子 mxx[i]res[i] m x x [ i ] , r e s [ i ] 存一下这条链为根的最大儿子。 dfs d f s 一遍即可。


代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;

int W,T,n,d[N],sz[N],ans[N];
int rem[N],mx[N],mxx[N],f[N],res[N];
int head[N],to[N<<1],nxt[N<<1],tot;

inline int rd()
{
    char ch=getchar();int x=0,f=1;
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

inline void dfs(int x)
{
    int i,j;
    d[x]=d[f[x]]+1;sz[x]=1;mx[x]=mxx[x]=0;
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(j==f[x]) continue;
        f[j]=x;dfs(j);sz[x]+=sz[j];
        if(sz[mx[x]]<sz[j]){mxx[x]=mx[x];mx[x]=j;}
        else if(sz[mxx[x]]<sz[j]) mxx[x]=j;
    }
    if(sz[x]-1-sz[mx[x]]>=rem[mx[x]]) rem[x]=((sz[x]-1)&1)+1;
    else rem[x]=rem[mx[x]]-sz[x]+2+sz[mx[x]];
}

inline void df(int x)
{
    int i,j;ans[x]=0;
    if(!((sz[1]-d[x])&1)){
        j=res[x];if(sz[mx[x]]>sz[res[x]]) j=mx[x];
        if((sz[1]-d[x]-sz[j])>=rem[j]) ans[x]=1;
    }
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(j==f[x]) continue;
        res[j]=res[x];
        if((j!=mx[x])&&(sz[mx[x]]>sz[res[j]])) res[j]=mx[x];
        else if(sz[mxx[x]]>sz[res[j]]) res[j]=mxx[x];
        df(j);
    }
}

int main(){
    int i,j,ix,iy;
    W=rd(),T=rd();
    while(T--){
        n=rd();
        for(tot=0,i=1;i<=n;++i) head[i]=0;
        for(i=1;i<n;++i){ix=rd();iy=rd();lk(ix,iy);lk(iy,ix);}
        dfs(1);
        if(W==3){
            if((!((sz[1]-1)&1)) && (sz[1]-1-sz[mx[1]])>=rem[mx[1]]) puts("1");
            else puts("0");
        }else{
            res[1]=0;df(1);
            for(i=1;i<=n;++i) printf("%c",'0'+ans[i]);
            puts("");
        }
    }
}
可并堆是一种支持合并操作的堆数据结构,常见的可并堆有左偏树、斜堆、二项堆等。对于 LOJ#P188 可并堆的问题,下面以左偏树为例给出解题思路和代码实现。 ### 解题思路 1. **左偏树的性质**: - 左偏树是一种可并堆,它满足堆性质(小根堆或大根堆),即每个节点的值小于(或大于)其子节点的值。 - 左偏树还满足左偏性质,即每个节点的左子树的距离(到最近的叶子节点的距离)不小于右子树的距离。 2. **合并操作**: - 合并两个左偏树时,比较两个根节点的值,将值较大的根节点的树合并到值较小的根节点的右子树中。 - 合并后,检查右子树的距离是否大于左子树的距离,如果是,则交换左右子树,以维护左偏性质。 3. **插入操作**: - 插入一个新节点可以看作是合并一个只有一个节点的左偏树和原左偏树。 4. **删除操作**: - 删除根节点后,将其左右子树合并成一个新的左偏树。 ### 代码实现 ```python class Node: def __init__(self, val): self.val = val self.left = None self.right = None self.dist = 0 def merge(x, y): if not x: return y if not y: return x if x.val > y.val: x, y = y, x x.right = merge(x.right, y) if not x.left or (x.right and x.left.dist < x.right.dist): x.left, x.right = x.right, x.left x.dist = (x.right.dist + 1) if x.right else 0 return x def insert(root, val): new_node = Node(val) return merge(root, new_node) def delete(root): return merge(root.left, root.right) # 示例使用 root = None root = insert(root, 3) root = insert(root, 1) root = insert(root, 5) print(root.val) # 输出堆顶元素 root = delete(root) print(root.val) # 输出删除堆顶元素后的堆顶元素 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值