UOJ462 新年的小黄鸭【线段树,dp】

给定 (n) 个点的树,要求一种树剖方案使得"代价"尽可能小。

(nle 10^5)


(f_u) 表示只考虑 (u) 的子树时的代价,枚举重链 ((u,v)),其中 (v) 是叶子。

代价有两部分,一部分是重链连出的子树的 (f+siz) 之和,还有一部分是重链的代价:设重链是 (u=a_0,cdots,a_d=v),则为 (siz_{a_1}+sum_{ige 0}siz_{a_{2^i+1}})。预处理出每个点的 (1)(2^i+1) 级儿子,就可以那贡献在自底而上的 dp 过程中处理成 dfs 序区间加。

按叶子 dfs 序建线段树,维护区间加、区间求 (min),时间复杂度 (O(nlog^2n)),空间 (O(nlog 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
", dp[1]);
}
原文地址:https://www.cnblogs.com/AThousandMoons/p/14932474.html