题意理解一颗n−1条主要边的树,然后增加了m条附加边.
我们只能删除一条主要边,一条附加边,一种边叫做主要边,一种边叫做附加边.
要求删除两条边后,这棵树不再是连通的.
我们需要统计,有多少种方案可以使得不连通,输出方案数.
算法解析
附加边到底有什么用处?
对于每一条连接x,y节点的(x,y),其实我们都可以认为这条边,连接了(x,y)这条路径上的所有点.
当没有了主要边的时候,其实附加边就是我们的主要边.
所以说,附加边(x,y),就是将树上x,y之间的路径上的每条主要边,都覆盖了一次.
因为当(x,y)路径上的任意一条主要边消失后,他都可以成为主要边,去维护连通性.
因此现在我们的问题模型转化了.
给定一个n−1条边的树,求每一条树边,被非树边覆盖了多少次
树边也就是主要边
非树边也就是附加边
那么这就是一个树上差分统计覆盖次数问题了.
每一条附加边,使得(x,y)节点的路径上,每一个节点的权值+1.
此时我们的问题,变成了如何统计方案数.
我们来好好地分类讨论一下主要边,身上的附加边.
1.主要边被覆盖了0次,即上面只有0条附加边.
我们发现删除完这条主要边后,随意删除一条附加边,我们都可以让树不连通.也就是m种方案.
只要删除(2,4)这条红边,那么随意一条附加边,都可以满足条件.
2.主要边覆盖1次,即上面只有一条附加边
我们发现删除完这条主要边后,我们只能删除这条主要边的附加边.也就是11种方案.
也就是删除咱们图上面的(3,7)红边,然后我们只能删除那条上面的紫色边.
3.主要边覆盖大于1次,即上面有多条附加边
我们发现,怎么删除,总能连通.于是0种方案.
#include<bits/stdc++.h> using namespace std; const int N=500010; struct node { int n,v; } e[N*2]; int h[N],f[N][20],t,d[N],x,y,n,m,ans,date[N]; void add(int u,int v) { t++; e[t].v=v; e[t].n=h[u]; h[u]=t; } void dfs(int x,int fa) { d[x]=d[fa]+1; f[x][0]=fa; for (int i=1; (1<<i)<=d[x]; i++) { f[x][i]=f[f[x][i-1]][i-1]; } for (int i=h[x]; i; i=e[i].n) { if (e[i].v!=fa) { dfs(e[i].v,x); } } } int lca(int x,int y) { if (d[x] < d[y]) { swap(x, y); } int h = d[x] - d[y], k = 0; while (h) { if (h & 1) { x = f[x][k]; } h >>= 1; k++; } if (x == y) { return x; } for (int k = 19; k >= 0; k--) { if (f[x][k] != f[y][k]) { x = f[x][k]; y = f[y][k]; } } return f[x][0]; } void query(int u,int fa) { for (int i = h[u]; i; i = e[i].n) { int v = e[i].v; if (v == fa) continue; query(v, u); date[u] += date[v]; if (date[v] == 0) ans += m; else if (date[v] == 1) ans++; } } void update(int x,int y) { date[x]++; date[y]++; date[lca(x, y)] -= 2; } int main() { scanf("%d%d", &n, &m); for (int i = 1, u, v; i < n; i++) { scanf("%d%d", &u, &v); add(u, v); add(v, u); } dfs(1, 0); for (int i = 1, u, v; i <= m; i++) { scanf("%d%d", &u, &v); update(u, v); } query(1, 0); printf("%d ",ans); }