【2022 省选训练赛 Contest 15 C】老园丁与小司机(左偏树)(DP)
作者:互联网
老园丁与小司机
题目链接:2022 省选训练赛 Contest 15 C
题目大意
给你一棵树,然后有一些路径(满足路径的两端的点的 LCA 是其中之一),每个路径有选的费用。
然后要你花费最小的费用使得每个边都在选的至少一条路径中,或输出无解。
思路
考虑到路径的特别,我们可以有这么一种 DP。
设 \(f_{i,j}\) 为解决了 \(i\) 的子树,并且也解决了一条往上到 \(j\) 的链的最小费用。
那每次合并两个子树就找到它们里面各自最小的,给对面加上,然后合并两个子树的值。
然后跟所有的儿子合并完之后,我们要把不合法的踢出(就是 \(j\) 的深度小于等于 \(i\),那到处理上面的点的时候它就不合法了)
(当然是 \(1\) 就不用踢了)
然后考虑能不能优化这个过程。
看到合并,求最小的,踢出不合法(而且你会发现每次就是不断踢深度最小就可以了)
那我们可以用可并堆【按深度排,然后维护子树最小值,子树加值用懒标记】,用左偏树来实现即可。
代码
#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 300000 + 100;
int n, m, rt[N], tot, deg[N];
vector <int> G[N];
vector <pair<int, int> > L[N];
struct ZP_Tree {
ll val[N], lzy[N], minn[N];
int ls[N], rs[N], h[N], deg[N];
void up(int now) {
minn[now] = val[now];
if (ls[now]) minn[now] = min(minn[now], minn[ls[now]]);
if (rs[now]) minn[now] = min(minn[now], minn[rs[now]]);
}
void downa(int x, ll k) {
val[x] += k; minn[x] += k; lzy[x] += k;
}
void down(int now) {
if (lzy[now]) {
if (ls[now]) downa(ls[now], lzy[now]);
if (rs[now]) downa(rs[now], lzy[now]);
lzy[now] = 0;
}
}
int merge(int x, int y) {
if (!x || !y) return x + y;
if (deg[x] < deg[y]) swap(x, y);
down(x);
rs[x] = merge(rs[x], y);
if (h[rs[x]] >= h[ls[x]]) swap(rs[x], ls[x]);
h[x] = h[rs[x]] + 1;
up(x); return x;
}
int delete_(int x) {
down(x); return merge(ls[x], rs[x]);
}
}T;
void dfs(int now, int father) {
deg[now] = deg[father] + 1;
for (int i = 0; i < G[now].size(); i++) {
int x = G[now][i]; if (x == father) continue;
dfs(x, now);
if (!rt[x]) {
printf("-1"); exit(0);
}
if (!rt[now]) {
rt[now] = rt[x]; continue;
}
ll vx = T.minn[rt[x]], vy = T.minn[rt[now]];
T.downa(rt[now], vx);
T.downa(rt[x], vy);
rt[now] = T.merge(rt[now], rt[x]);
}
ll va = T.minn[rt[now]];
for (int i = 0; i < L[now].size(); i++) {
int v = L[now][i].first, w = L[now][i].second;
++tot; T.deg[tot] = deg[v]; T.val[tot] = T.minn[tot] = va + w;
rt[now] = T.merge(rt[now], tot);
}
if (now != 1) {
while (rt[now] && T.deg[rt[now]] >= deg[now]) {
rt[now] = T.delete_(rt[now]);
}
}
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i < n; i++) {
int x, y; scanf("%d %d", &x, &y);
G[y].push_back(x); G[x].push_back(y);
}
for (int i = 1; i <= m; i++) {
int u, v, c; scanf("%d %d %d", &u, &v, &c);
L[u].push_back(make_pair(v, c));
}
dfs(1, 0);
printf("%lld", T.minn[rt[1]]);
return 0;
}
标签:rt,15,minn,Contest,int,rs,deg,now,左偏 来源: https://www.cnblogs.com/Sakura-TJH/p/16053991.html