P5912 [POI2004]JAS
*I. P5912 [POI2004]JAS
摘自 贪心专题 第三部分例题 I.
究极神仙题。
首先对问题进行转化:相当于我们需要求原树最浅的一个点分树深度。假设点
考虑这样一个贪心:我们记
根据点分树的结论答案不可能超过
const int N = 5e4 + 5;
const int K = 1 << 16;
int cnt, hd[N], nxt[N << 1], to[N << 1];
void add(int u, int v) {nxt[++cnt] = hd[u], hd[u] = cnt, to[cnt] = v;}
int n, f[N], S[N], lg[K], buc[K];
void dfs(int id, int fa) {
int msk = 0, ban = 0, leaf = 1;
for(int i = hd[id]; i; i = nxt[i]) {
int it = to[i];
if(it == fa) continue;
leaf = 0, dfs(it, id), f[id] = max(f[id], f[it]);
msk |= ban & S[it], ban |= S[it];
} if(leaf) return S[id] = 1, void();
msk = (K - (1 << lg[msk])) & (K - 1 - ban);
int c = !msk ? lg[n] + 1 : buc[msk & -msk];
f[id] = max(f[id], c);
S[id] = (ban & (K - (1 << c))) | (1 << c);
}
int main(){
cin >> n;
for(int i = 2; i < K; i++) lg[i] = lg[i >> 1] + 1;
for(int i = 1; i < 16; i++) buc[1 << i] = i;
for(int i = 1; i < n; i++) {
int u = read(), v = read();
add(u, v), add(v, u);
} dfs(1, 0), cout << f[1] << endl;
return 0;
}
启示:遇到无从下手的问题时先尝试抽象问题并分析性质(本题中是两点间的简单路径必然存在点分树的 LCA)使其更好理解。树上问题先从叶子开始,可以先考虑一些简单情况再尝试总结策略。树形 DP 先考虑仅合并两个子树的情况。