Loading

【题解】CF671D-Roads in Yusland

给定一棵以 \(1\) 为根的树,给定 \(m\) 条路径,每条路径覆盖 \(x_i\)\(y_i\),权值为 \(w_i\),保证 \(y_i\)\(x_i\) 祖先,求覆盖整棵树的最小路径权值和。

首先我们不难想出一个一个树形 DP,用 \(f_{x,d}\) 表示覆盖了 \(x\) 的子树,并且向上衍生到深度 \(d\) 的答案。根据套路我们可以直接线段树优化 DP,时空复杂度是 \(\mathcal{O}(n\log n)\),空间可能有点卡。

我们可以换一种方式维护。对于每个 \(x\),开一个堆维护二元组 \(\{value,dep\}\) 表示有一个代价为 \(value\)\(f_{x,d}\) 方案。合并子树时,只用支持整体加即可。启发式合并可以做到时空复杂度 \(\mathcal{O}(m\log ^2 m + n) - \mathcal{O}(n + m)\)。用左偏树可以做到 \(\mathcal{O}(m \log m + n) - \mathcal{O}(n + m)\)

#define N 300005
int n, m, d[N]; LL f[N], p[N];
vector<int>e[N];
vector<Pr> c[N];
struct node{
	LL val; int dep;
	bool operator<(const node o)const{return val < o.val;}
};
multiset<node>s[N];
void merge(int x,int y){
	if(si(s[x]) < si(s[y]))swap(s[x], s[y]), swap(p[x], p[y]);
	go(w, s[y])s[x].insert(node{w.val + p[y] - p[x], w.dep});
	s[y].clear();
}
void check(int x){
	while(!s[x].empty() && (*s[x].begin()).dep >= d[x])s[x].erase(s[x].begin());
}
void dfs(int x,int fa){
	d[x] = d[fa] + 1;
	go(y, e[x])if(y != fa)dfs(y, x), f[x] += f[y];
	go(y, e[x])if(y != fa)p[y] += f[x] - f[y], merge(x, y);
	go(y, c[x])s[x].insert(node{y.se + f[x] - p[x], d[y.fi]});
	check(x); if(s[x].empty()){puts("-1"); exit(0);}
	f[x] = (*s[x].begin()).val + p[x];
}
int main() {
	read(n, m);
	rp(i, n - 1){
		int x, y; read(x, y);
		e[x].pb(y), e[y].pb(x);
	}
	while(m--){
		int x, y, z; read(x, y, z);
		c[x].pb(Pr{y, z});
	}
	LL ans = 0;
	go(y, e[1])dfs(y, 1), ans += f[y];
	cout << ans << endl;
	return 0;
}
posted @ 2022-06-04 23:00  7KByte  阅读(31)  评论(0编辑  收藏  举报