[CF708C]Centroids

708C:Centroids

题意简述

给你一棵 n 个结点的树,你有一次机会把一条边断掉然后在任意位置接上一条边,但要保证操作之后还是一棵树。
现在求每个点进行这种操作之后是否可能成为重心,即所有子树大小都不超过n2的点。

数据范围

1n4105

思路

考虑某一个结点 x 可能成为重心的条件。
注意到一个重要的性质,一个结点size>n2的孩子最多只有一个。
那么肯定是 x 的那个孩子的子树中有一条边被切了,并且两边sizen2
那么树形DP。
son[i] 为结点 i size最大的儿子。
f[i] 为结点 i 的子树中,以某一节点为根的子树的sizen2的最大值。
pos[i] f[i] 的取值在 i 的哪一个孩子的子树中。
se[i]为结点 i 的子树中,以某一节点为根的子树sizen2的次大值,要求和 f[i] 不能在 i 同一个孩子的子树中。
g[i]为结点 i 的非子树中(第一步往父亲走),以某一节点为根的子树的sizen2的最大值。
转移什么的看代码吧…
特别麻烦= =

代码

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct edge{
    int s,t,next;
}e[800010];
int head[400010],cnt;
void addedge(int s,int t)
{
    e[cnt].s=s;e[cnt].t=t;e[cnt].next=head[s];head[s]=cnt++;
    e[cnt].s=t;e[cnt].t=s;e[cnt].next=head[t];head[t]=cnt++;
}
int n,u,v,siz;
int son[400010],size[400010],f[400010],se[400010],pos[400010],fa[400010],g[400010];
bool ok[400010];
void dfs(int node,int lastfa)
{
    fa[node]=lastfa;
    size[node]=1;
    son[node]=0;
    for (int i=head[node];i!=-1;i=e[i].next)
        if (e[i].t!=lastfa)
        {
            dfs(e[i].t,node);
            size[node]+=size[e[i].t];
            if (size[e[i].t]>size[son[node]])
                son[node]=e[i].t;
            if (size[e[i].t]<=n/2)
            {
                if (size[e[i].t]>f[node])
                {
                    se[node]=f[node];
                    f[node]=size[e[i].t];
                    pos[node]=e[i].t;
                }
                else if (size[e[i].t]>se[node])
                    se[node]=size[e[i].t];
            }
            else if (f[e[i].t]>f[node])
            {
                se[node]=f[node];
                f[node]=f[e[i].t];
                pos[node]=e[i].t;
            }
            else if (f[e[i].t]>se[node])
                se[node]=f[e[i].t];
        }
    if (n-size[node]>size[son[node]])
        son[node]=lastfa;
}
void dfs2(int node,int lastfa)
{
    for (int i=head[node];i!=-1;i=e[i].next)
        if (e[i].t!=lastfa)
        {
            if (n-size[e[i].t]<=n/2)
                g[e[i].t]=max(g[e[i].t],n-size[e[i].t]);
            g[e[i].t]=max(g[e[i].t],g[node]);
            if (pos[node]==e[i].t)
                g[e[i].t]=max(g[e[i].t],se[node]);
            else
                g[e[i].t]=max(g[e[i].t],f[node]);
            dfs2(e[i].t,node);
        }
}
int main()
{
    scanf("%d",&n);
    memset(head,0xff,sizeof(head));
    cnt=0;
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        addedge(u,v);
    }
    dfs(1,1);
    dfs2(1,1);
    for (int i=1;i<=n;i++)
        if (son[i]==fa[i])
            ok[i]=(n-size[i]-g[i]<=n/2);
        else
            ok[i]=(size[son[i]]-f[i]<=n/2);
    for (int i=1;i<=n;i++)
        printf("%d ",ok[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值