zoukankan      html  css  js  c++  java
  • 【CF708C】Centroids 题解(树形DP)

    做到了一个没见过的idea的题,幸甚至哉,歌以咏志。

    ------------------

    题目大意:给定一颗树,你有一次将树改造的机会,改造的意思是删去一条边,再加入一条边,保证改造后还是一棵树。请问有多少点可以通过改造,成为这颗树的重心?

    首先有一个显然的事实:如果一个点是原本树的中心,那么不用管它;如果这个点经过改造后可以成为树的重心,那么必然有且只有一个子树的大小大于$lfloor frac{n}{2} floor$。而我们改造这个点的方法是:找这个重儿子最大的小于等于$lfloor frac{n}{2} floor$的子树,并将它插到这个点上。

    所以我们要DP来求上述这个子树的大小。设$f_i$表示最大的小于等于$lfloor frac{n}{2} floor$的子树的大小。有转移:

    $f_u=max(size_v) (size_v < lfloor frac{n}{2} floor)$

    $f_u=max(f_v) (size_v > lfloor frac{n}{2} floor)$

    同时我们设$g_u$表示$f_u$所指的$u$。上述的操作都可以$O(n)$解决。这样我们就解决了子树内的情况。

    对于子树外的点,我们设$h_i$表示不在子树中的最大取值。有转移:

    $h_v=max(n-size_u) (n-size_u < lfloor frac{n}{2} floor)$

    $h_v=max(h_u) (n-size_u > lfloor frac{n}{2} floor)$

    如果$f_u$的最佳转移点是$v$,那么$h_v$与$f_u$的次大值取$max$;反则与最大值取$max$。

    最后判断是否合法:如果重儿子大小大于$lfloor frac{n}{2} floor$,那么就看重儿子大小减去$f_{son_u}$是否小于等于$lfloor frac{n}{2} floor$;若$n-size_u > lfloor frac{n}{2} floor$,那么看$n-size_u$与$h_u$的差值是否小于等于$lfloor frac{n}{2} floor$。

    时间复杂度$O(n)$。

    代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    const int maxn=400005;
    int f[maxn][2],g[maxn],h[maxn],son[maxn],size[maxn],ans[maxn],n;
    int head[maxn],cnt;
    struct node
    {
        int next,to;
    }edge[maxn*2];
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void add(int from,int to)
    {
        edge[++cnt].next=head[from];
        edge[cnt].to=to;
        head[from]=cnt;
    }
    inline void dfs_pre(int now,int fa)
    {
        size[now]=1;
        for (int i=head[now];i;i=edge[i].next)
        {
            int to=edge[i].to;
            if (to==fa) continue;
            dfs_pre(to,now);
            size[now]+=size[to];
            if (size[to]>size[son[now]]) son[now]=to;
            int cur=(size[to]<=n/2)?size[to]:f[to][0];
            if (cur>f[now][0])
            {
                f[now][1]=f[now][0];
                f[now][0]=cur;
                g[now]=to;
            }
            else if (cur>f[now][1]) f[now][1]=cur;
        }
    }
    inline void dfs_dp(int now,int fa)
    {
        ans[now]=1;
        if (size[son[now]]>n/2)
            ans[now]=size[son[now]]-f[son[now]][0]<=n/2;
        else if (n-size[now]>n/2)
            ans[now]=n-size[now]-h[now]<=n/2;
        for (int i=head[now];i;i=edge[i].next)
        {
            int to=edge[i].to;
            if (to==fa) continue;
            int cur = n-size[now] <= n/2 ? n-size[now] : h[now];
            h[to]=max(h[to],cur);
            h[to]=max(h[to],f[now][g[now]==to]);
            dfs_dp(to,now);
        }
    }
    int main()
    {
        n=read();
        for (int i=1;i<n;i++)
        {
            int x=read(),y=read();
            add(x,y);add(y,x);
        }
        dfs_pre(1,0);
        dfs_dp(1,0);
        for (int i=1;i<=n;i++) printf("%d ",ans[i]);
        return 0;
    }
  • 相关阅读:
    JSON字符串转对象,List集合,需要的jar 包
    VirtualBox 更改虚拟磁盘大小
    SpringBoot webSocket 发送广播、点对点消息,Android接收
    window系统下添加 glassfish 的系统服务
    idea把java web项目打成war包
    栈类型数据的运用
    leetcode实践:找出两个有序数组的中位数
    leetcode实践:通过链表存储两数之和
    通过监控Nginx日志来实时屏蔽高频恶意访问的IP
    Java版分布式ID生成器技术介绍
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13702062.html
Copyright © 2011-2022 走看看