Loj题解 #10131 暗的连锁
前置芝士
-
树上差分
-
倍增/树剖 求 LCA
Description
\(n\) 个结点,有 \(n - 1\) 条边为树边, \(m\) 条边为非树边。减掉一个树边和一个非树边使图不连通,问方案数。
数据范围: \(n \le 1e5, m \le 2e5\)
Solution
考虑一条非树边 \((u, v)\) 的贡献:
- 会形成一个环
- 如果选择减掉 \(u \to v\) 上的边,那么非树边只能选这条边才能满足题目要求
我们称每条附加边 \((u, v)\) 都把树上 \(u, v\) 间的路径覆盖了一次。
显然,被覆盖了两次的边一定没有可行方案;
被覆盖了一次的边只能有一种方案;
没有被覆盖的边可以选任意一条非树边,有 \(m\) 种方案。
所以用树上差分统计出每条树边被覆盖了几次,最后枚举一遍求每条边的贡献即可
用树上差分时,假设加一条边 \((u,v)\)
那么 \(cnt_u ++, cnt_v++, cnt_{lca(u, v)} -= 2\)
然后 DFS 回溯统计即可
(自己画个图手模一下)
Code
/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 2e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
struct edge{
int to, nxt;
}e[MAXN << 1];
int head[MAXN], num_edge = 0;
int n, m, Ans = 0;
int cnt[MAXN], ans[MAXN];
int dep[MAXN], top[MAXN], fath[MAXN], siz[MAXN], son[MAXN];
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}
void add_edge(int from, int to) { e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }
void dfs(int u, int fa){
fath[u] = fa, dep[u] = dep[fa] + 1, siz[u] = 1;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
dfs(v, u);
siz[u] += siz[v];
if(siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs2(int u, int tp) {
top[u] = tp;
if(son[u]) dfs2(son[u], tp);
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fath[u] || v == son[u]) continue;
dfs2(v, v);
}
}
int Get_Lca(int u, int v) {
while(top[u] != top[v]) dep[top[u]] < dep[top[v]] ? v = fath[top[v]] : u = fath[top[u]];
return dep[u] < dep[v] ? u : v;
}
void Dfs(int u, int fa) {
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
ans[v] = cnt[v];
Dfs(v, u);
ans[u] += ans[v];
}
}
int main()
{
n = read(), m = read();
for(int i = 1, u, v; i < n; ++i) {
u = read(), v = read();
add_edge(u, v), add_edge(v, u);
}
dfs(1, 0), dfs2(1, 1);
for(int i = 1, u, v; i <= m; ++i) {
u = read(), v = read();
int lca = Get_Lca(u, v);
cnt[u] ++, cnt[v] ++, cnt[lca] -= 2;
}
Dfs(1, 0);
for(int i = 1; i <= n; ++i) {
if(ans[i] == 1) Ans ++;
if(ans[i] == 0) Ans += m;
}
printf("%d", Ans);
return 0;
}
鸣谢
- 《信息学奥赛一本通—提高篇》
我对不起 Kersen