1. 程式人生 > >「日常訓練」Balancing Act(POJ-1655)

「日常訓練」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;
}