「POJ-3417」Network (LCA+树上差分)

「POJ-3417」Network
LCA+树上差分,给定一棵树和一些附加边,在其中各删一条使树不连通,求可行的方案数。

题意

给定一棵树和一些附加边,要求在原始边和附加边中各选一条删除,从而使树被分割为至少两个联通块,求可行的方案数。

思路

对于每个给出的附加边求LCA,树上差分,标记其所在的环上的所有边的经过次数。

对于树上每条边的经过次数:

  1. $cnt[i]=0:$ 不属于任何环,此时只要删除该边和任意一条附加边即可,方案数$+m;$

  2. $cnt[i]=1:$ 只属于一个环,删除该边和属于该环的附加边为可行解,方案数$+1;$

  3. $cnt[i]>1:$该边属于多个环,需要删除该边和所在所有边上的附加环才可分割该图,删该树边不存在可行解。

代码

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
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int maxn=1e5+10;

struct Edge{int nex,to; }edge[maxn<<1];

int n,head[maxn],dep[maxn],fa[maxn][30],cnt;

void add(int u,int v)
{
edge[++cnt].nex=head[u];
edge[cnt].to=v;
head[u]=cnt;
}

void dfs(int u,int pre)
{
dep[u]=dep[pre]+1;
fa[u][0]=pre;
for(int i=1;(1<<i)<=n;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
for(int i=head[u];i;i=edge[i].nex) if(edge[i].to!=pre) dfs(edge[i].to,u);
}

int LCA(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
int d=dep[u]-dep[v];
for(int i=0;(1<<i)<=d;i++) if((1<<i)&d) u=fa[u][i];
if(u==v) return u;
for(int i=20;i>=0;i--)
{
if(fa[u][i]!=fa[v][i])
{
u=fa[u][i];
v=fa[v][i];
}
}
return fa[u][0];
}

int sta[maxn];

int dfs1(int u,int pre)
{
for(int i=head[u];i;i=edge[i].nex) if(edge[i].to!=pre) sta[u]+=dfs1(edge[i].to,u);
return sta[u];
}

int main()
{
int m,u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
add(u,v); add(v,u);
}
dfs(1,0);
for(int i=0;i<m;i++)
{
scanf("%d%d",&u,&v);
int root=LCA(u,v);
sta[u]++; sta[v]++;
sta[root]-=2;
}
dfs1(1,0);
int ans=0;
for(int i=2;i<=n;i++)
{
if(sta[i]==0) ans+=m;
else if(sta[i]==1) ans++;
}
printf("%d\n",ans);
return 0;
}