CSP-S2019 D2T3 樹的重心
題目
題目描述
小簡單正在學習離散數學,今天的內容是圖論基礎,在課上他做了如下兩條筆記:
- 一個大小爲 的樹由 個結點與 條無向邊構成,且滿足任意兩個結點間有且僅有一條簡單路徑。在樹中刪去一個結點及與它關聯的邊,樹將分裂爲若干個子樹;而在樹中刪去一條邊(保留關聯結點,下同),樹將分裂爲恰好兩個子樹。
- 對於一個大小爲 的樹與任意一個樹中結點 ,稱 是該樹的重心當且僅當在樹中刪去 及與它關聯的邊後,分裂出的所有子樹的大小均不超過 (其中 是下取整函數)。對於包含至少一個結點的樹,它的重心只可能有 或 個。
課後老師給出了一個大小爲 的樹 ,樹中結點從 編號。小簡單的課後作業是求出 單獨刪去每條邊後,分裂出的兩個子樹的重心編號和之和。即:
上式中, 表示樹 的邊集, 表示一條連接 號點和 號點的邊。 與 分別表示樹 刪去邊 後, 號點與 號點所在的被分裂出的子樹。
小簡單覺得作業並不簡單,只好向你求助,請你教教他。
分析
40 分的做法是枚舉一條邊,將它斷掉後再暴力求出兩邊的重心。
結論: 重心必須在以根節點爲起點的重鏈上。
證明: 不會。。。
然後我們可以利用這個結論,枚舉每一個點,統計每個點在刪掉邊後對答案的貢獻次數即可。
當我們分裂出一棵完整的子樹時,就像這樣:
對於以 爲根的子樹的這部分,根據結論,我們沿着以 爲起點的重鏈往下跳就可以了。
對於剩餘的部分,我們考慮換根。將剩餘的部分提成以 爲根的樹,然後暴力修改重兒子和重鏈即可。
我們可以用倍增來優化尋找重心的這一部分。
參考代碼
#include <cstdio>
#include <algorithm>
using namespace std;
const int Maxn = 300000;
const int Maxlog = 19;
typedef long long ll;
#ifdef LOACL
bool ___;
#endif
struct Edge {
int to;
Edge *nxt;
};
Edge pool[Maxn * 2 + 5];
Edge *G[Maxn + 5], *ecnt;
inline void addedge(int u, int v) {
Edge *p = ++ecnt;
p->to = v, p->nxt = G[u];
G[u] = p;
}
int N;
int siz[Maxn + 5];
int mxson[Maxn + 5], mx2son[Maxn + 5];
ll ans;
int s[Maxn + 1][Maxlog + 2];
int f[Maxn + 5];
inline void clear() {
for(int i = 1; i <= N; i++)
mxson[i] = mx2son[i] = 0;
for(int i = 1; i <= N; i++)
for(int j = 0; j <= Maxlog; j++)
s[i][j] = 0;
for(int i = 1; i <= N; i++)
G[i] = NULL;
ecnt = &pool[0];
ans = 0;
}
inline void modify(int u) {
for(int i = 1; i <= Maxlog; i++)
s[u][i] = s[s[u][i - 1]][i - 1];
}
void PreDFS(int u, int fa) {
siz[u] = 1, f[u] = fa;
for(Edge *p = G[u]; p != NULL; p = p->nxt) {
int v = p->to;
if(v == fa) continue;
PreDFS(v, u);
siz[u] += siz[v];
if(siz[v] > siz[mxson[u]]) {
mx2son[u] = mxson[u];
mxson[u] = v;
} else if(siz[v] > siz[mx2son[u]])
mx2son[u] = v;
}
s[u][0] = mxson[u];
modify(u);
}
inline void calc(int u) {
int tot = siz[u], rt = u;
for(int i = Maxlog; i >= 0; i--)
if(tot - siz[s[u][i]] <= tot / 2)
u = s[u][i];
if(siz[s[u][0]] <= tot / 2) ans += u;
if(u != rt && siz[u] <= tot / 2) ans += f[u];
}
void DFS(int u, int fa) {
if(fa != 0) calc(fa), calc(u);
int flag = 0, t1;
if(N - siz[u] > siz[mxson[u]]) {
t1 = mx2son[u], flag = 1;
mx2son[u] = mxson[u], mxson[u] = fa;
} else if(N - siz[u] > siz[mx2son[u]]){
t1 = mx2son[u], flag = 2;
mx2son[u] = fa;
}
for(Edge *p = G[u]; p != NULL; p = p->nxt) {
int v = p->to;
if(v == fa) continue;
siz[u] += siz[fa] - siz[v], f[u] = v;
if(v == mxson[u]) s[u][0] = mx2son[u];
else s[u][0] = mxson[u];
modify(u);
DFS(v, u);
siz[u] -= siz[fa] - siz[v], f[u] = fa;
}
if(flag == 1) {
mxson[u] = mx2son[u], mx2son[u] = t1;
} else if(flag == 2) mx2son[u] = t1;
s[u][0] = mxson[u];
modify(u);
}
#ifdef LOACL
bool ____;
#endif
int main() {
#ifdef LOACL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int _;
scanf("%d", &_);
while(_--) {
scanf("%d", &N);
clear();
for(int i = 1; i < N; i++) {
int u, v;
scanf("%d %d", &u, &v);
addedge(u, v), addedge(v, u);
}
PreDFS(1, 0);
DFS(1, 0);
printf("%lld\n", ans);
}
#ifdef LOACL
printf("%.2f\n", (&____ - &___) / 1048576.0);
#endif
return 0;
}