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]);
}
上一篇:分析数据摘要算法的效率性能(SHA、MD5和CRC32)


下一篇:Java Opensource ETL框架具有自动调度功能