闇の連鎖

闇の連鎖

传说中的暗之连锁被人们称为 Dark。

Dark 是人类内心的黑暗的产物,古今中外的勇者们都试图打倒它。

经过研究,你发现 Dark 呈现无向图的结构,图中有 N 个节点和两类边,一类边被称为主要边,而另一类被称为附加边。

Dark 有 N1 条主要边,并且 Dark 的任意两个节点之间都存在一条只由主要边构成的路径。

另外,Dark 还有 M 条附加边。

你的任务是把 Dark 斩为不连通的两部分。

一开始 Dark 的附加边都处于无敌状态,你只能选择一条主要边切断。

一旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切断。

但是你的能力只能再切断 Dark 的一条附加边。

现在你想要知道,一共有多少种方案可以击败 Dark。

注意,就算你第一步切断主要边之后就已经把 Dark 斩为两截,你也需要切断一条附加边才算击败了 Dark。

输入格式

第一行包含两个整数 NM

之后 N1 行,每行包括两个整数 AB,表示 AB 之间有一条主要边。

之后 M 行以同样的格式给出附加边。

输出格式

输出一个整数表示答案。

数据范围

N100000, M200000,数据保证答案不超过 2311

输入样例:

4 1
1 2
2 3
1 4
3 4

输出样例:

3

 

解题思路

  首先只考虑主要边那么就会构成一棵树,其中对于任意一条附加边如果将其添加到树中那么就会构成一个环。如下图,其中红色的是附加边:

  那么如果要砍掉这个环上的一条主要边(即图中绿色的边),为了使得整个图不连通那么就要砍掉这条红色的边(附加边)。

  现在再往图中加一条附加边:

  对于新的附加边所构成的环,如果砍掉这个环上的一条标记了1的主要边,那么只有再砍掉这条新的附加边才能使得整个图不连通。

  其中边上的数字x表示如果要砍掉这条主要边,那么还需要砍掉x条附加边才能使得整个图不连通。因此可以枚举每一条附加边,由该条附加边与主要边所构成的环上的每一条主要边都累加上1,表示砍掉这条主要边后要使得整个图不连通还要再砍该条附加边。

  在枚举完所有的附加边后,再遍历一遍由主要边构成的树。对于树上的每一条边,假设这条边累加的数字为x

  • 如果x=0,则表示砍完这条边后不需要再砍任何附加边就可以使得整个图不连通,而由于必须要砍一条附加边,因此可以任选一条附加边砍掉,因此答案累加m
  • 如果x=1,则表示砍完这条边后还要再砍一条附加边才使得整个图不连通,那么只能砍掉这条附加边,只有一种方案,答案累加1
  • 如果x>1,则表示砍完这条边后还要再砍x条附加边才使得整个图不连通,而由于只能砍一条附加边,因此无解。

  因此这题的核心问题就是如何快速的给树上某条路径上的边都加上一个数。可以类比一维差分,可以实现给某个连续区间都加上同一个数。在树上的话就是树上差分的问题。

  对于一棵树每个节点都有权值wu,那么节点u所对应的差分数组是du=wuvson(u)wv,即节点u的权值减去所有儿子的权值。那么如何通过差分数组得到每个节点原始(或者修改后)的权值呢?对于节点u,只需求出以u为根的整个子树关于差分数组的和。

  这个可以用数学归纳法来证明,如果u是叶子节点,那么du=wu,以u为根的整个子树关于差分数组的和就是du。现在假设u不是叶子节点,其子节点为vson(u),根据归纳法以v为根的整个子树关于差分数组的和为wv,那么以u为根的整个子树关于差分数组的和就是du+vson(u)wv=wu,归纳假设成立。

  先介绍点差分的概念。

  如果要给树中从点st的路径上的每一个点都加上c,那么需要先求出st的最近公共祖先lca,同时lca的父节点为p,然后{ds   += cdlca = cdt   += cdp   = c

  这里借用OI Wiki的图:

  可以认为公式中的前两条是对蓝色方框内的路径进行操作,后两条是对红色方框内的路径进行操作。并且这样做可以保证最终只有st的路径上的点加上c。首先对于节点p,根据树上差分的定义,以p为根的整个子树关于差分数组的和加上2c又减去2c,因此不变,同理包括从p往上走的节点也是。而不包含st的子树自然也不会受到影响,因此只有路径上的点都被加了一次c

  然后是边差分。

  我们知道树中除了根节点外每个节点都含有一个父节点,那么将从节点u到其父节点的这条边归属到节点u,因此树中的n1条边就可以用除根节点的n1个节点编号来表示了。因此st的路径上的每一条边都加上c,就可以转换成给对应的点加上c,这就变成了上面点差分的问题。

  比如下图,st的路径上的边就分别对应标记了1的节点,因此对这些边加上c就等价于对标记了1的节点加上c

{ds   += cdt   += cdlca = 2c

  其中这题用到的是边差分。

  AC代码如下,时间复杂度为O(n+mlogn)

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 1e5 + 10, M = N * 2;
 5 
 6 int n, m;
 7 int head[N], e[M], ne[M], idx;
 8 int fa[N][17], dep[N];
 9 int q[N], hh, tt = -1;
10 int d[N];
11 
12 void add(int v, int w) {
13     e[idx] = w, ne[idx] = head[v], head[v] = idx++;
14 }
15 
16 int lca(int a, int b) {
17     if (dep[a] < dep[b]) swap(a, b);
18     for (int i = 16; i >= 0; i--) {
19         if (dep[fa[a][i]] >= dep[b]) a = fa[a][i];
20     }
21     if (a == b) return a;
22     for (int i = 16; i >= 0; i--) {
23         if (fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i];
24     }
25     return fa[a][0];
26 }
27 
28 int dfs(int u, int pre) {
29     int ret = 0;
30     for (int i = head[u]; i != -1; i = ne[i]) {
31         if (e[i] != pre) {
32             ret += dfs(e[i], u);
33             d[u] += d[e[i]];
34         }
35     }
36     if (pre != -1) {
37         if (!d[u]) ret += m;
38         else if (d[u] == 1) ret++;
39     }
40     return ret;
41 }
42 
43 int main() {
44     scanf("%d %d", &n, &m);
45     memset(head, -1, sizeof(head));
46     for (int i = 0; i < n - 1; i++) {
47         int v, w;
48         scanf("%d %d", &v, &w);
49         add(v, w), add(w, v);
50     }
51     memset(dep, 0x3f, sizeof(dep));
52     dep[0] = 0, dep[1] = 1;
53     q[++tt] = 1;
54     while (hh <= tt) {
55         int t = q[hh++];
56         for (int i = head[t]; i != -1; i = ne[i]) {
57             if (dep[e[i]] > dep[t] + 1) {
58                 dep[e[i]] = dep[t] + 1;
59                 q[++tt] = e[i];
60                 fa[e[i]][0] = t;
61                 for (int j = 1; j <= 16; j++) {
62                     fa[e[i]][j] = fa[fa[e[i]][j - 1]][j - 1];
63                 }
64             }
65         }
66     }
67     for (int i = 0; i < m; i++) {
68         int a, b;
69         scanf("%d %d", &a, &b);
70         d[a]++, d[b]++, d[lca(a, b)] -= 2;
71     }
72     printf("%d", dfs(1, -1));
73     
74     return 0;
75 }
复制代码

 

参考资料

  AcWing 352. 闇の連鎖(算法提高课):https://www.acwing.com/video/571/

  前缀和 & 差分 - OI Wiki:https://oi-wiki.org/basic/prefix-sum/

posted @   onlyblues  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示