「日常训练」Balancing Act(POJ-1655)

题意与分析

树的重心板子题。
值得考虑的是,重心究竟有哪些优秀的性质?
这里是一些网上能看到的性质:

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

利用判定性质能够在一次dfs内完成树的重心的判断。定义\(dp[i]\)是第i个节点所拥有的子树的节点的最小值,更新即可。具体看代码。
相关题目见之后的题解。

代码

#include <iostream>
#include <cstring>
#include <utility>
#include <vector>

#define INF 0x3f3f3f3f
#define PB push_back
#define MP make_pair
#define fi first
#define se second
#define rep(i,a,b) for(repType i=(a); i<=(b); ++i)
#define per(i,a,b) for(repType i=(a); i>=(b); --i)
#define ZERO(x) memset(x, 0, sizeof(x))
#define MS(x,y) memset(x, y, sizeof(x))
#define ALL(x) (x).begin(), (x).end()

#define QUICKIO                  \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)

using namespace std;
typedef int repType;

const int MAXN=20005;
vector<int> G[MAXN];
int cnt[MAXN],dp[MAXN];
int n;
int dfs(int x)
{
    if(cnt[x]!=0) return 0;
    cnt[x]=1;
    rep(i,0,int(G[x].size())-1)
    {
        cnt[x]+=dfs(G[x][i]);
        dp[x]=max(dp[x],cnt[G[x][i]]);
    }
    dp[x]=max(dp[x],n-cnt[x]);
    return cnt[x];
}

int
main()
{
    int T; cin>>T;
    while(T--)
    {
        cin>>n;
        rep(i,1,n) G[i].clear();
        rep(i,1,n-1)
        {
            int u,v; cin>>u>>v;
            G[u].PB(v);
            G[v].PB(u);
        }
        MS(cnt,0);
        MS(dp,-1);
        rep(i,1,n)
            if(!cnt[i]) dfs(i);
        int ans=INF,pos=0;
        rep(i,1,n)
            if(ans>dp[i])
                ans=dp[pos=i];
        cout<<pos<<" "<<ans<<endl;
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/samhx/p/poj-1655.html