[BZOJ3772]精神污染
试题描述
兵库县位于日本列岛的中央位置,北临日本海,南面濑户内海直通太平洋,中央部位是森林和山地,与拥有关西机场的大阪府比邻而居,是关西地区面积最大的县,是集经济和文化于一体的一大地区,是日本西部门户,海陆空交通设施发达。濑户内海沿岸气候温暖,多晴天,有日本少见的贸易良港神户港所在的神户市和曾是豪族城邑“城下町”的姬路市等大城市,还有以疗养地而闻名的六甲山地等。
兵库县官方也大力发展旅游,为了方便,他们在县内的N个旅游景点上建立了n-1条观光道,构成了一棵图论中的树。同时他们推出了M条观光线路,每条线路由两个节点x和y指定,经过的旅游景点就是树上x到y的唯一路径上的点。保证一条路径只出现一次。
你和你的朋友打算前往兵库县旅游,但旅行社还没有告知你们最终选择的观光线路是哪一条(假设是线路A)。这时候你得到了一个消息:在兵库北有一群丧心病狂的香菜蜜,他们已经选定了一条观光线路(假设是线路B),对这条路线上的所有景点都释放了【精神污染】。这个计划还有可能影响其他的线路,比如有四个景点1-2-3-4,而【精神污染】的路径是1-4,那么1-3,2-4,1-2等路径也被视为被完全污染了。
现在你想知道的是,假设随便选择两条不同的路径A和B,存在一条路径使得如果这条路径被污染,另一条路径也被污染的概率。换句话说,一条路径被另一条路径包含的概率。
输入
第一行两个整数N,M
接下来N-1行,每行两个数a,b,表示A和B之间有一条观光道。
接下来M行,每行两个数x,y,表示一条旅游线路。
输出
所求的概率,以最简分数形式输出。
输入示例
5 3 1 2 2 3 3 4 2 5 3 5 2 5 1 4
输出示例
1/3
数据规模及约定
100%的数据满足:N,M<=100000
题解
先膜拜一下 popoqqq 的题解。
然后我再试图自己讲讲。。。
一个很重要东西:一棵树上,一条链 A 被另一条链 B 包含当且仅当 A 的两个端点都在 B 上。
那么对于一条链,要知道它是否被包含,只需要关心它的两个端点就行了。
对于一条链 (a, b),我们不妨把 b 称为 a 的对面儿。查询一条链 C 包含了哪些链的方法就是把所有在链 C 上的节点拎出来,分别统计它们的对面儿在不在链 C 上,而这个在链 C 上的“对面儿”们的个数就是 C 包含的链的条数。
所以我们不妨建立一个树形的主席树,每个节点 u 在其父节点的基础之上把 u 的“对面儿”们的信息加进来。
这个信息,具体指什么信息呢?我们发现我们需要回答有几个点在一条链上,所以可以对整棵树搞一个括号序列,那么这个信息,存节点在括号序列对应的位置就好了,注意一个节点在括号序列中对应两个位置,左括号的位置 +1,右括号的位置 -1。
注意括号序列只能处理自己到祖先这样的链,所以对于一条 (a, b) 的链,在询问它包含几条链时把它拆成 (a, lca(a, b)) 和 (b, lca(a, b)) 这两条链就好了,小心 lca(a, b) 这个点有可能被重复统计,写代码时细心一点。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 100010 #define maxm 200010 #define maxnode 4000010 #define maxlog 17 #define LL long long int ToT, sumv[maxnode], lc[maxnode], rc[maxnode]; void update(int& y, int x, int l, int r, int p, int v) { sumv[y = ++ToT] = sumv[x] + v; if(l == r) return ; int mid = l + r >> 1; lc[y] = lc[x]; rc[y] = rc[x]; if(p <= mid) update(lc[y], lc[x], l, mid, p, v); else update(rc[y], rc[x], mid + 1, r, p, v); return ; } int query(int o, int l, int r, int ql, int qr) { if(!o) return 0; if(ql <= l && r <= qr) return sumv[o]; int mid = l + r >> 1, ans = 0; if(ql <= mid) ans += query(lc[o], l, mid, ql, qr); if(qr > mid) ans += query(rc[o], mid + 1, r, ql, qr); return ans; } int n, m, head[maxn], next[maxm], to[maxm]; void AddEdge(int a, int b) { to[++m] = b; next[m] = head[a]; head[a] = m; swap(a, b); to[++m] = b; next[m] = head[a]; head[a] = m; return ; } struct Path { int a, b; Path() {} Path(int _, int __): a(_), b(__) {} } ps[maxn]; int pm, phead[maxn], pnxt[maxn], pto[maxn]; void AddPath(int a, int b) { ps[++pm] = Path(a, b); pto[pm] = b; pnxt[pm] = phead[a]; phead[a] = pm; return ; } int fa[maxlog][maxn], dep[maxn], rt[maxn], dl[maxn], dr[maxn], clo; void build(int u) { dl[u] = ++clo; for(int i = 1; i < maxlog; i++) fa[i][u] = fa[i-1][fa[i-1][u]]; for(int e = head[u]; e; e = next[e]) if(to[e] != fa[0][u]) { dep[to[e]] = dep[u] + 1; fa[0][to[e]] = u; build(to[e]); } dr[u] = ++clo; return ; } int lca(int a, int b) { if(dep[a] < dep[b]) swap(a, b); for(int i = maxlog - 1; i >= 0; i--) if(dep[a] - dep[b] >= (1 << i)) a = fa[i][a]; for(int i = maxlog - 1; i >= 0; i--) if(fa[i][a] != fa[i][b]) a = fa[i][a], b = fa[i][b]; return a == b ? a : fa[0][b]; } void build2(int u) { rt[u] = rt[fa[0][u]]; for(int e = phead[u]; e; e = pnxt[e]) { update(rt[u], rt[u], 1, clo, dl[pto[e]], 1), update(rt[u], rt[u], 1, clo, dr[pto[e]], -1); // if(ToT % 10000 == 0) printf("ToT: %d ", ToT); } for(int e = head[u]; e; e = next[e]) if(to[e] != fa[0][u]) build2(to[e]); return ; } LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } int main() { // freopen("data.in", "r", stdin); n = read(); int tm = read(); for(int i = 1; i < n; i++) { int a = read(), b = read(); AddEdge(a, b); } while(tm--) { int a = read(), b = read(); AddPath(a, b); } build(1); build2(1); LL ans = 0; for(int i = 1; i <= pm; i++) { int a = ps[i].a, b = ps[i].b, c = lca(a, b); // printf("lca(%d, %d) = %d ", a, b, c); ans += (LL)query(rt[a], 1, clo, dl[c], dl[a]) - query(rt[fa[0][c]], 1, clo, dl[c], dl[a]); ans += (LL)query(rt[a], 1, clo, dl[c] + 1, dl[b]) - query(rt[fa[0][c]], 1, clo, dl[c] + 1, dl[b]); ans += (LL)query(rt[b], 1, clo, dl[c], dl[a]) - query(rt[c], 1, clo, dl[c], dl[a]); ans += (LL)query(rt[b], 1, clo, dl[c] + 1, dl[b]) - query(rt[c], 1, clo, dl[c] + 1, dl[b]); ans--; } LL bns = (LL)pm * (pm - 1) >> 1, g = gcd(ans, bns); ans /= g; bns /= g; printf("%lld/%lld ", ans, bns); return 0; }