P5327 [ZJOI2019] 语言
P5327 [ZJOI2019] 语言
最开始读错了题, 不然真有可能自己做出来, 遗憾。 考虑一个性质, 每次更新的语言都不一样, 那么这条链上的每个点对都可以互相进行贸易, 那么如果一个点在多条链上, 那它与这些链的所有人都可以贸易, 所以考虑点 \(x\) 的贡献, 就是所有覆盖 \(x\) 的链构成的最小连通子树大小 - 1。现在有一个很常见的结论。
我们有所有覆盖点 \(x\) 链的两端点, 把这些点按 \(dfs\) 序排序, 那么有
最小连通子树大小等于 $\sum dep(u) + dep(v) - dep(lca(u, v)) $ 最后再减去 \(dep(结尾, 开头)\)
考虑每个点都要算这玩意, 但是每次都暴力统计浪费了太多信息, 再想到哪些信息可以保留, 如果我们把路径加改成差分加子树和, 就可以从儿子继承信息, 并可以得出整个数的答案, 考虑差分可行性, 一条链 \((u, v)\) 可分成 \((u, lca(u, v))\), \((v, lca(u, v))\) 两半, 而这两半分别是 \(u\) 和 \(v\) 的祖先。
所以可做, 线段树合并即可。
码了一个多小时, 还借助了题解代码, 我真是fw
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
#define int long long
int n, m, head[N], tot, rt[N];
struct Edge{ int to, next; }edge[N << 1];
void add(int x, int y) {
edge[++tot] = {y, head[x]}; head[x] = tot;
edge[++tot] = {x, head[y]}; head[y] = tot;
}
int dfn[N], tim, st[20][N], lg[N * 2] = {-1}, fa[N], dep[N], id[N * 2];
void dfs(int u, int pre) {
dfn[u] = ++tim, st[0][tim] = u, fa[u] = pre;
dep[u] = dep[pre] + 1, id[tim] = u;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (v == pre) continue;
dfs(v, u);
st[0][++tim] = u;
}
}
void build_st() {
for (int i = 1; i <= lg[tim]; i++) {
for (int j = 1; j + (1 << i) - 1 <= tim; j++) {
int x = st[i - 1][j], y = st[i - 1][j + (1 << (i - 1))];
st[i][j] = dep[x] < dep[y] ? x : y;
}
}
}
int lca(int x, int y) {
if (x == 0 || y == 0) return 0;
x = dfn[x], y = dfn[y];
if (x > y) swap(x, y);
int k = lg[y - x + 1];
return dep[st[k][x]] < dep[st[k][y - (1 << k) + 1]] ? st[k][x] : st[k][y - (1 << k) + 1];
}
vector<int> del[N];
#define pb push_back
struct SegT{
struct Node{
int ls, rs, mx, mn, sum, is;
}t[N * 80];
int tot;
#define ls(p) (t[p].ls)
#define rs(p) (t[p].rs)
#define mid (l + r >> 1)
void pushup(int p) {
t[p].sum = t[ls(p)].sum + t[rs(p)].sum - dep[lca(id[t[ls(p)].mx], id[t[rs(p)].mn])];
t[p].mn = t[ls(p)].mn ? t[ls(p)].mn : t[rs(p)].mn;
t[p].mx = t[rs(p)].mx ? t[rs(p)].mx : t[ls(p)].mx;
}
void modify(int &p, int l, int r, int x, int y) {
if (!p) p = ++tot;
if (l == r) {
t[p].is += y;
if (t[p].is > 0) t[p].mn = t[p].mx = x, t[p].sum = dep[id[x]];
if (t[p].is <= 0) t[p].mn = t[p].mx = t[p].sum = 0;
return;
}
if (x <= mid) modify(ls(p), l, mid, x, y);
if (x > mid) modify(rs(p), mid + 1, r, x, y);
pushup(p);
}
int query(int p) { return t[p].sum - dep[lca(id[t[p].mn], id[t[p].mx])]; }
void merge(int &p1, int p2, int l, int r) {
if (!p1 || !p2) return p1 |= p2, void();
if (l == r) {
t[p1].is += t[p2].is;
if (t[p1].is > 0) t[p1].mn = t[p1].mx = l, t[p1].sum = dep[id[l]];
if (t[p1].is <= 0) t[p1].mn = t[p1].mx = t[p1].sum = 0;;
return;
}
merge(ls(p1), ls(p2), l, mid); merge(rs(p1), rs(p2), mid + 1, r);
pushup(p1);
}
}T;
int ans;
void work(int u, int pre) {
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (v == pre) continue;
work(v, u);
T.merge(rt[u], rt[v], 1, tim);
}
for (auto i : del[u]) T.modify(rt[u], 1, tim, dfn[i], -1);
ans += T.query(rt[u]);
}
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n * 2; i++) lg[i] = lg[i / 2] + 1;
for (int i = 1, x, y; i <= n - 1; i++) {
scanf("%lld%lld", &x, &y);
add(x, y);
}
dfs(1, 0); build_st();
for (int i = 1, u, v, _lca; i <= m; i++) {
scanf("%lld%lld", &u, &v); _lca = lca(u, v);
del[_lca].pb(u); del[_lca].pb(v);
del[fa[_lca]].pb(u); del[fa[_lca]].pb(v);
T.modify(rt[u], 1, tim, dfn[u], 1);
T.modify(rt[u], 1, tim, dfn[v], 1);
T.modify(rt[v], 1, tim, dfn[v], 1);
T.modify(rt[v], 1, tim, dfn[u], 1);
}
work(1, 0); printf("%lld", ans / 2);
return 0;
}

浙公网安备 33010602011771号