小王子

题目大意:在一棵无根树上加上M条新边,然后能毁掉两条边,规定一条是树边,一条是新边,问有多少种方案能使树断裂。

每次加上一条新边(u,v),必定会形成一个环\(u-lca(u,v)-v-u\),给这个路径上的边的覆盖数都认为加1,考虑加完之后:

如果一条树边的覆盖数为0,显然断掉这个之后,再随便断掉一条新边都可以使得树不联通,所以贡献为m

如果一条树边的覆盖数为1,显然断掉这个之后,再断掉覆盖在他这条边上的那个新边就可以使得树不联通,贡献为1

如果一条树边的覆盖树大于1,那显然无贡献

如何统计一个边的覆盖数?树上差分

首先给树定个方向,选1为根,令\(g[i]\)表示以i为终点(在树中深度较大的点)的边的覆盖数,那么每次只要新加一条新边\((u,v)\)

只需要更新\(u-lca(u,v)\)\(v-lca(u,v)\)两条路径上的边就可以了

\(++g[u],++g[v],g[lca(u,v)]-=2\)

最后再来一遍树形dp,自上而下统计一遍就好

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e5 + 66;

inline int read()
{
int s(0), w(1);
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}

inline void put(int x)
{
if (! x) putchar('0');
if (x < 0) putchar('-'), x = -x;
int num(0); char c[66];
while (x) c[++ num] = x % 10 + 48, x /= 10;
while (num) putchar(c[num --]);
return (void)(putchar('\n'));
}

int to[N << 1], nex[N << 1], head[N], cnt;

inline void add_edge(int x, int y)
{
to[++ cnt] = y;
nex[cnt] = head[x];
return (void)(head[x] = cnt);
}

int n, m, res;
int g[N], dep[N], fa[N][66], vis[N];

inline void dfs(int x)
{
int i, y;
for (i = 1; i <= 30; ++ i)
{
if (fa[x][i - 1]) fa[x][i] = fa[fa[x][i - 1]][i - 1];
else break;
}

for (i = head[x]; i; i = nex[i])
{
y = to[i];
if (y == fa[x][0]) continue;
fa[y][0] = x;
dep[y] = dep[x] + 1;
dfs(y);
}
}

inline int lca(int x, int y)
{
if (dep[x] < dep[y]) swap(x, y);
int delta = dep[x] - dep[y], i;
for (i = 30; i >= 0; -- i)
{
if (delta & (1 << i))
x = fa[x][i];
}
if (x == y) return x;
for (i = 30; i >= 0; -- i)
{
if (fa[x][i] != fa[y][i])
{
x = fa[x][i], y = fa[y][i];
}
}
return fa[x][0];
}

inline void dp(int x)
{
int i, y;
vis[x] = 1;
for (i = head[x]; i; i = nex[i])
{
y = to[i];
if (! vis[y])
{
dp(y);
g[x] += g[y];
}
}
}

signed main()
{
int i, x, y;
n = read(), m = read();
for (i = 1; i < n; ++ i)
{
x = read(), y = read();
add_edge(x, y), add_edge(y, x);
}
dfs(1);
for (i = 1; i <= m; ++ i)
{
x = read(), y = read();
++ g[x], ++ g[y], g[lca(x, y)] -= 2;
}
dp(1);
for (i = 2; i <= n; ++ i)
{
if (! g[i]) res += m;
else if (g[i] == 1) ++ res;
}
put(res);
return 0;
}