传送门

这是找树的重心的经典题目。

题目大意:在一棵树中找一个点,删掉以后使所有子树中最大的节点数最少,问应该删哪个点以及最大子树的节点数。

思路:树形dp,f[u]保存以u为根节点的子树的节点数,那么只要比较答案和f[v]+1和n-f[u]-1的关系就可以了。

dfs一遍,重点在f[u]+=dfs1(v)+w;更新f[u],bl=max(bl,f[v]+1);和bl=max(bl,nf[u]1);更新bl。

树的重心有下面几条常见性质:

定义1:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心
定义2:以这个点为根,那么所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。
性质1:树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
性质2:把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
性质3:把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
struct node{
int v,w;
};
vector <node>adj[20010];
int n,ans,bans;
int f[20010];
bool vis[20010];
int dfs1(int u)
{
    vis[u]=true;
    f[u]=0;
    int bl=0;
    for(int i=0; i<adj[u].size(); ++i)
    {
        int v = adj[u][i].v;
        int w = adj[u][i].w;
        if(vis[v])
            continue;
        f[u]+=dfs1(v)+w;
        bl=max(bl,f[v]+1);
    }
    bl=max(bl,n-f[u]-1);
    if (bl<bans)
    {
        bans=bl;
        ans=u;
    }
    return f[u];
}
int main()
{
    int w;
    scanf("%d",&w);
    while (w--)
    {
        scanf("%d",&n);
        for(int i=1; i<=n; ++i)
            adj[i].clear();
        for(int u=2; u<=n; ++u)
        {
            int v, w;
            scanf("%d%d", &v, &w);
            adj[v].push_back((node){w, 1});
            adj[w].push_back((node){v, 1});
        }
        memset(f,0,sizeof(f));
        memset(vis,false,sizeof(vis));
        ans=bans=0x3f3f3f3f;
        dfs1(1);
        printf("%d %d\n",ans,bans);
    }
    return 0;
}

 

Latest posts by 青鸟千秋 (see all)

CF-1025D Recovering BST(区间dp)

传送门 题意:给定n个整数,问是否能生成一棵BST(二分查找树,左子树都比它小,右子树都比它大),并且有边相连的点权的gcd>1。 思路:区间dp,dp[l][...

阅读全文

牛客网暑期多校第五场E room(费用流)

传送门 题意:有n个宿舍,每个宿舍住4个人,给定去年n个宿舍的人员构成,今年n个4人组合的情况,求最少几个人需要搬寝室。 思路:转化为最小费用最大流解...

阅读全文

HDU-4091 Zombie’s Treasure Chest(暴力)

传送门 题意:有一个容量为n的箱子,和无限多的两种珠宝,体积和价值分别是s1,v1,s2,v2,求能装下的最大价值。 思路:直接暴力枚举会TLE,先计算s1 s2的LC...

阅读全文

欢迎留言