不是本人的思路,学习自 秦淮岸灯火阑珊 大佬,
https://www.acwing.com/solution/acwing/content/2830/
题目描述
传说中的暗之连锁被人们称为 Dark。
Dark 是人类内心的黑暗的产物,古今中外的勇者们都试图打倒它。
经过研究,你发现 Dark 呈现无向图的结构,图中有 N 个节点和两类边,一类边被称为主要边,而另一类被称为附加边。
Dark 有 N – 1 条主要边,并且 Dark 的任意两个节点之间都存在一条只由主要边构成的路径。
另外,Dark 还有 M 条附加边。
你的任务是把 Dark 斩为不连通的两部分。
一开始 Dark 的附加边都处于无敌状态,你只能选择一条主要边切断。
一旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切断。
但是你的能力只能再切断 Dark 的一条附加边。
现在你想要知道,一共有多少种方案可以击败 Dark。
注意,就算你第一步切断主要边之后就已经把 Dark 斩为两截,你也需要切断一条附加边才算击败了 Dark。
输入格式
第一行包含两个整数 N 和 M。
之后 N – 1 行,每行包括两个整数 A 和 B,表示 A 和 B 之间有一条主要边。
之后 M 行以同样的格式给出附加边。
输出格式
输出一个整数表示答案。
数据范围
N≤100000,M≤200000,数据保证答案不超过231−1
输入样例:
4 1
1 2
2 3
1 4
3 4
输出样例:
3
树的差分,先分析是点的差分,还是边的差分,题目提示很明显是边的差分,
思路:把树的主边建立起来,之后的附加边做边差分;
变换:把边的差分赋给点,点的值代表的是某条边终点的覆盖情况,即剪完这主条边,还要剪多少条负边
因为这个点代表是是这条边的孩子(终点),所以最后只要深搜下这颗树,每次看看以该结点为起点(父亲)
的边若要剪去有几种做法,深搜尝试所有的主边,(每个结点作为终点结束的边),得到最终答案;
C++ 代码
#include<iostream>
const int space = 1e5 + 7;
//struct { int a, b; }imp_edge[space];
int head[space] = {}, nxt[space << 2] = {}, ver[space << 2] = {};
int deep[space] = {}, ST[space][52] = { {} }, tot = 0;//ST表一直数组越界,导致答案错误,打log表,或者开大数组
//或者算出精确的数值例如2^18比1e5更大,于是从18逆推
int N, M, ans = 0, is_on[space] = {};
void add_edge(int a, int b) { ver[++tot] = b, nxt[tot] = head[a], head[a] = tot; }
void mswap(int& a, int& b) { int tmp = a; a = b; b = tmp; }
int LCA(int u, int v)
{
if (deep[u] < deep[v])mswap(u, v);
for (int i = 19; i >= 0; i--)
if (deep[ST[u][i]] >= deep[v])u = ST[u][i];
if (u == v)return u;
for (int i = 19; i >= 0; i--)
if (ST[u][i] != ST[v][i])
u = ST[u][i], v = ST[v][i];
return ST[u][0];
}
void DFS(int son, int father)
{
deep[son] = deep[father] + 1;
ST[son][0] = father;
for (int i = 1; (1 << i) <= deep[son]; i++)
ST[son][i] = ST[ST[son][i - 1]][i - 1];
for (int i = head[son]; i; i = nxt[i])
{
int grs = ver[i];
if (father == grs)continue;
DFS(grs, son);
}
return;
}
void find_ans(int son, int father)
{
for (int i = head[son]; i; i = nxt[i])
{
int grs = ver[i];
if (grs == father)continue;
find_ans(grs, son);
is_on[son] += is_on[grs];
if (!is_on[grs])ans += M;
else if (1 == is_on[grs])ans++;
}
}
void updata(int u, int v)
{
is_on[u]++;
is_on[v]++;
is_on[LCA(u, v)] -= 2;//边的的差分
//is_on[LCA(u, v)]--, is_on[ST[LCA(u, v)][0]]--;点的差分
}
int main(void)
{
std::ios_base::sync_with_stdio(false);
std::cin >> N >> M;
for (int i = 1; i < N; i++)
{
int a, b;
std::cin >> a >> b;
add_edge(a, b), add_edge(b, a);
}
DFS(1, 0);
for (int i = 0; i < M; i++)
{
int a, b;
std::cin >> a >> b;
updata(a, b);
}
find_ans(1, 0);
std::cout << ans;
return 0;
}
大佬简化大佬的题解,让题目更加亲人