• 【CF 708C】Centroids


    题目简述

    给定一棵 \(n\) 个点的树,你可以删除一条边并增加一条边,形成一棵新树。

    问每个点在进行这样的操作后,是否可能成为新树的重心。

    \(1\le n\le4\cdot10^5\)

    思路

    要让每个子树大小都小于等于 \(\left\lfloor\dfrac {n} {2}\right\rfloor\),如果如果这个子树本身就可以作为重心,就不用改变。

    考虑如果本身不能作为重心,说明一定有且只有一个子树大小大于 \(\left\lfloor\dfrac {n} {2}\right\rfloor\)

    我们一定是从这个子树里面选一个子树接在当前的根上面,否则不是最优的。那么就要从该子树里面找到一个小于等于 \(\left\lfloor\dfrac {n} {2}\right\rfloor\) 的最大子树,然后就可以进行判断了。

    考虑如何快速求出这个值。

    看上去就很像一个换根 DP。

    先考虑 \(dp_u\)\(u\) 子树内内该值的大小。对于他的儿子 \(v\),有如下方程:

    \[f(x)= \begin{cases} \max(siz_v)& siz_v\le\left\lfloor\dfrac {n} {2}\right\rfloor\\ \max(dp_v)& \text{otherwise} \end{cases} \]

    考虑换根,那我们要得到子树外能移除最大子树的大小。

    考虑 \(dp'_v\) 如何得到。考虑 \(dp_u\) 的最佳转移点,如果 \(dp_u\) 最佳转移点就是 \(v\) ,那么我们就需要得到一个点第二大的能够去除的子树大小。所以必须考虑维护两个 dp 值,\(dp_{u,0}\) 表示第一大的值,\(dp_{u,1}\) 表示第二大的值。

    然后如果 \(v\)\(u\) 最佳转移点,那么 \(dp'_v\) 可取值为 \(dp[u][1]\)\(n-siz_u(n-siz_u<=\left\lfloor\dfrac {n} {2}\right\rfloor)\),否则可取值为 \(dp_{u,0}\)\(n-siz_u(n-siz_u<=\left\lfloor\dfrac{n}{2}\right\rfloor)\)

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+5;
    struct edge {
        int to,nxt;
    } e[N<<1];
    int n,cnt,head[N],siz[N],maxsiz[N],f[N][2],pos[N];
    inline void add(int u,int v) {
        e[++cnt].nxt=head[u],head[u]=cnt,e[cnt].to=v;
    }
    void dfs1(int u,int fa) {
        siz[u]=1;
        for(int i=head[u]; i; i=e[i].nxt) {
            if(e[i].to==fa) {
                continue;
            }
            dfs1(e[i].to,u),siz[u]+=siz[e[i].to];
            int v=f[e[i].to][0];
            if(siz[e[i].to]>siz[maxsiz[u]]) {
                maxsiz[u]=e[i].to;
            }
            if(siz[e[i].to]<=n/2) {
                v=siz[e[i].to];
            }
            if(f[u][0]<v) {
                f[u][1]=f[u][0],f[u][0]=v,pos[u]=e[i].to;
            } else if(f[u][1]<v) {
                f[u][1]=v;
            }
        }
    }
    int ans[N],dp[N];
    void dfs2(int u,int fa) {
        ans[u]=1;
        if(siz[maxsiz[u]]>n/2) {
            ans[u]=(siz[maxsiz[u]]-f[maxsiz[u]][0]<=n/2);
        } else if(n-siz[u]>n/2) {
            ans[u]=(n-siz[u]-dp[u]<=n/2);
        }
        for(int i=head[u]; i; i=e[i].nxt) {
            if(e[i].to==fa) {
                continue;
            }
            int v=n-siz[u];
            if(n-siz[u]>n/2) {
                v=dp[u];
            }
            dp[e[i].to]=max(dp[e[i].to],v);
            if(pos[u]==e[i].to) {
                dp[e[i].to]=max(dp[e[i].to],f[u][1]);
            } else {
                dp[e[i].to]=max(dp[e[i].to],f[u][0]);
            }
            dfs2(e[i].to,u);
        }
    }
    int main() {
        scanf("%d",&n);
        for(int i=1,u,v; i<=n-1; i++) {
            scanf("%d %d",&u,&v),add(u,v),add(v,u);
        }
        dfs1(1,0),dfs2(1,0);
        for(int i=1; i<=n; i++) {
            printf("%d ",ans[i]);
        }
        return 0;
    }
    

    本文作者:AFewMoon,文章地址:https://www.cnblogs.com/AFewMoon/p/15485288.html

    知识共享许可协议

    本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。

    限于本人水平,如果文章有表述不当之处,还请不吝赐教。

  • 相关阅读:
    初窥AndroidPlot(一)
    Android Studio安装、配置
    Android SDK Android NDK Android Studio 官方下载地址及方式
    学习Android图表绘制
    ActionBar研究(二)
    获取Android版本信息和程序版本号
    Android手机获取通话记录和短信记录
    Android开发之监听或获取手机短信内容【转】
    android 几种发送短信的方法【转】
    android开源框架android-async-http
  • 原文地址:https://www.cnblogs.com/AFewMoon/p/15485288.html
Copyright © 2020-2023  润新知