[ZJOI2019]语言

先讲一个智障 3log 做法,听说考场上不止一个人写还都过了。

树剖,转化为 \((u,v)\)\(dfs\) 序若都在一个区间内则它们可以开展贸易活动。相当于求矩形总面积,可以扫描线。每次树剖会拆分出 \(O(\log n)\) 个区间,也即 \(O(\log^2 n)\) 个矩形。时间复杂度 \(O(n\log^3n)\)

扯点正经的:

常规操作,考虑一个点 \(u\) 能到达的所有点,就是所有经过它的链的两个端点拿出来建个虚树,求虚树大小。

找所有经过它的链可以树上差分,对于链 \(u,v\)\(u,v\) 加入它,在 \(lca(u,v)\) 的父亲处删除它(删两次)。

虚树大小的经典结论就是按 dfs 序排序后求距离总和除以二。所以拿个 set 随便维护就行了。启发式合并,复杂度 \(O(n\log^2 n)\)

但是发现可以用线段树合并替代,trick*2,时间复杂度降为 \(O(n\log n)\)。因为线段树很多时候可以代替平衡树,所以遇到平衡树启发式合并考虑线段树合并砍掉一个 log。

哦对了要写 \(O(n\log n)-O(1)\) lca 才能做到 \(O(n\log n)\)感觉比3log还好写。

#include <cstdio>
#include <vector>
#include <cstring>
#define gc (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++)
#define lson(p) (tree[p].ls)
#define rson(p) (tree[p].rs)

const int INF = 1e9;
char buf[100000], *p1, *p2;
inline int read() {
	char ch;
	int x = 0;
	while ((ch = gc) < 48);
	do x = x * 10 + ch - 48; while ((ch = gc) >= 48);
	return x;
}

struct Edge {int to, nxt;} e[200005];
int head[100005], dfn[100005], dep[100005], st[18][200005], fa[100005], tot, timer, len;
int Log[200005], fst[100005], root[100005], pnt[100005], cnt, n, m;
bool mark[1005];
std::vector<int> ins[100005], del[100005];
long long ans;
struct Node {
	int l, r, sum, ls, rs, num;
	Node() {l = INF, r = sum = num = ls = rs = 0;}
} tree[4000005];
inline void AddEdge(int u, int v) {
	e[++ tot].to = v, e[tot].nxt = head[u], head[u] = tot;
}
void dfs(int u) {
	pnt[dfn[u] = ++ timer] = u, dep[u] = dep[fa[u]] + 1;
	st[0][++ len] = u, fst[u] = len;
	for (int i = head[u]; i; i = e[i].nxt) if (e[i].to != fa[u]) fa[e[i].to] = u, dfs(e[i].to), st[0][++ len] = u;
}
inline int LCA(int u, int v) {
	u = fst[u], v = fst[v];
	if (u > v) u ^= v ^= u ^= v;
	int k = Log[v - u + 1];
	return dep[st[k][u]] <= dep[st[k][v - (1 << k) + 1]] ? st[k][u] : st[k][v - (1 << k) + 1];
}
inline int dis(int u, int v) {
  	return dep[u] + dep[v] - 2 * dep[LCA(u, v)];
}
inline void pushup(int p) {
	tree[p].l = std::min(tree[lson(p)].l, tree[rson(p)].l);
	tree[p].r = std::max(tree[lson(p)].r, tree[rson(p)].r);
	tree[p].sum = tree[lson(p)].sum + tree[rson(p)].sum +
	(tree[lson(p)].r && tree[rson(p)].l < INF ? dis(pnt[tree[lson(p)].r], pnt[tree[rson(p)].l]) : 0);
}
void insert(int &p, int l, int r, int v) {
	if (!p) p = ++ cnt;
	if (l == r) {tree[p].l = tree[p].r = v, ++ tree[p].num; return;}
	int mid = l + r >> 1;
	if (v <= mid) insert(lson(p), l, mid, v);
	else insert(rson(p), mid + 1, r, v);
	pushup(p);
}
void remove(int &p, int l, int r, int v) {
	if (l == r) {if (!(-- tree[p].num)) tree[p].l = INF, tree[p].r = 0; return;} 
	int mid = l + r >> 1;
	if (v <= mid) remove(lson(p), l, mid, v);
	else remove(rson(p), mid + 1, r, v);
	pushup(p);
}
void merge(int &u, int &v, int l, int r) {
	if (!u || !v) {u |= v; return;}
	if (l == r) {
		tree[u].num += tree[v].num;
		if (tree[u].num) tree[u].l = tree[u].r = l;
		return;
	}
	merge(lson(u), lson(v), l, l + r >> 1);
	merge(rson(u), rson(v), (l + r >> 1) + 1, r);
	pushup(u);
}
void solve(int u) {
	for (int i = head[u]; i; i = e[i].nxt)
		if (e[i].to != fa[u]) solve(e[i].to), merge(root[u], root[e[i].to], 1, n);
	for (int i : ins[u]) insert(root[u], 1, n, dfn[i]);
	for (int i : del[u]) remove(root[u], 1, n, dfn[i]);
	ans += tree[root[u]].sum;
	if (tree[root[u]].l < tree[root[u]].r)
		ans += dis(pnt[tree[root[u]].l], pnt[tree[root[u]].r]);
}

int main() {
	n = read(), m = read();
	for (int i = 1, u, v; i < n; ++ i) u = read(), v = read(), AddEdge(u, v), AddEdge(v, u);
	for (int i = 2; i <= 2 * n; ++ i) Log[i] = Log[i >> 1] + 1;
	dfs(1);
	for (int i = 1; i <= 17; ++ i)
	for (int j = 1; j + (1 << i) - 1 <= len; ++ j)
		if (dep[st[i - 1][j]] < dep[st[i - 1][j + (1 << i - 1)]]) st[i][j] = st[i - 1][j];
		else st[i][j] = st[i - 1][j + (1 << i - 1)];
	for (int i = 1, u, v; i <= m; ++ i) {
		u = read(), v = read();
		int lca = fa[LCA(u, v)];
		ins[u].push_back(u), ins[u].push_back(v), ins[v].push_back(u), ins[v].push_back(v);
		del[lca].push_back(u), del[lca].push_back(v), del[lca].push_back(u), del[lca].push_back(v);
	}
	solve(1);
	printf("%lld", ans >> 2);
	return 0;
}
posted @ 2022-08-21 21:36  zqs2020  阅读(29)  评论(0编辑  收藏  举报