1. 程式人生 > 其它 >UOJ462 新年的小黃鴨【線段樹,dp】

UOJ462 新年的小黃鴨【線段樹,dp】

給定 \(n\) 個點的樹,要求一種樹剖方案使得"代價"儘可能小。

\(n\le 10^5\)


\(f_u\) 表示只考慮 \(u\) 的子樹時的代價,列舉重鏈 \((u,v)\),其中 \(v\) 是葉子。

代價有兩部分,一部分是重鏈連出的子樹的 \(f+siz\) 之和,還有一部分是重鏈的代價:設重鏈是 \(u=a_0,\cdots,a_d=v\),則為 \(siz_{a_1}+\sum_{i\ge 0}siz_{a_{2^i+1}}\)。預處理出每個點的 \(1\)\(2^i+1\) 級兒子,就可以那貢獻在自底而上的 dp 過程中處理成 dfs 序區間加。

按葉子 dfs 序建線段樹,維護區間加、區間求 \(\min\)

,時間複雜度 \(O(n\log^2n)\),空間 \(O(n\log n)\)

#include<bits/stdc++.h>
#define PB emplace_back
using namespace std;
const int N = 100003, M = 1<<18;
template<typename T>
void rd(T &x){
	int ch = getchar(); x = 0;
	for(;ch < '0' || ch > '9';ch = getchar());
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int n, dfn[N], out[N], siz[N], tim, fa[17][N], seg[M], tag[M], dp[N];
vector<int> son[N], G[N];
void ptag(int x, int v){seg[x] += v; tag[x] += v;}
void pdown(int x){
	if(tag[x]){
		ptag(x<<1, tag[x]);
		ptag(x<<1|1, tag[x]);
		tag[x] = 0;
		return;
	}
}
void upd(int l, int r, int v, int x = 1, int L = 1, int R = tim){
	if(l <= L && R <= r){ptag(x, v); return;}
	int md = L+R>>1; pdown(x);
	if(l <= md) upd(l, r, v, x<<1, L, md);
	if(md < r) upd(l, r, v, x<<1|1, md+1, R);
	seg[x] = min(seg[x<<1], seg[x<<1|1]);
}
int qry(int l, int r, int x = 1, int L = 1, int R = tim){
	if(l <= L && R <= r) return seg[x];
	int md = L+R>>1; pdown(x);
	if(r <= md) return qry(l, r, x<<1, L, md);
	if(md < l) return qry(l, r, x<<1|1, md+1, R);
	return min(qry(l, r, x<<1, L, md), qry(l, r, x<<1|1, md+1, R));
}
void dfs1(int x){
	siz[x] = 1;
	for(int i = 0;i < 16;++ i)
		fa[i+1][x] = fa[i][fa[i][x]];
	if(x > 1) son[fa[0][x]].PB(x);
	for(int i = 0, v;i < 17;++ i)
		if(v = fa[0][fa[i][x]]) son[v].PB(x);
	dfn[x] = tim+1;
	if(x > 1 && G[x].size() == 1) ++tim;
	for(int v : G[x]) if(v != fa[0][x]){
		fa[0][v] = x; dfs1(v); siz[x] += siz[v];
	} out[x] = tim;
}
void dfs2(int x){
	int sum = 0;
	for(int v : G[x]) if(v != fa[0][x]){
		dfs2(v); sum += siz[v] + dp[v];
	}
	for(int v : G[x]) if(v != fa[0][x])
		upd(dfn[v], out[v], sum-siz[v]-dp[v]);
	for(int v : son[x]) upd(dfn[v], out[v], siz[v]);
	dp[x] = qry(dfn[x], out[x]);
	for(int v : son[x]) upd(dfn[v], out[v], -siz[v]);
}
int main(){
	rd(n);
	for(int i = 1, u, v;i < n;++ i){
		rd(u); rd(v);
		G[u].PB(v); G[v].PB(u); 
	} dfs1(1); dfs2(1);
	printf("%d\n", dp[1]);
}