题目描述
传说中的“闇の連鎖”被人们称为
D
a
r
k
Dark
Dark。
D
a
r
k
Dark
Dark 是人类内心的黑暗的产物,古今中外的勇者们都试图打倒它。经过研究,你发现
D
a
r
k
Dark
Dark呈现无向图的结构,图中有
N
N
N 个节点和两类边,一类边被称为主要边,而另一类被称为附加边。
D
a
r
k
Dark
Dark 有
N
–
1
N – 1
N–1条主要边,并且
D
a
r
k
Dark
Dark 的任意两个节点之间都存在一条只由主要边构成的路径。另外,
D
a
r
k
Dark
Dark 还有
M
M
M条附加边。你的任务是把
D
a
r
k
Dark
Dark 斩为不连通的两部分。一开始
D
a
r
k
Dark
Dark的附加边都处于无敌状态,你只能选择一条主要边切断。一旦你切断了一条主要边,
D
a
r
k
Dark
Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切断。但是你的能力只能再切断
D
a
r
k
Dark
Dark 的一条附加边。现在你想要知道,一共有多少种方案可以击败
D
a
r
k
Dark
Dark。注意,就算你第一步切断主要边之后就已经把
D
a
r
k
Dark
Dark 斩为两截,你也需要切断一条附加边才算击败了
D
a
r
k
Dark
Dark。
(
N
≤
1
0
5
,
M
≤
2
∗
1
0
5
)
(N\leq 10^5,M\leq 2*10^5)
(N≤105,M≤2∗105)
首先分析题目所给的树结构,“主要边”构成了一棵树,“附加边”则是非树边,把一条非树边添加到数中则会形成一个环,那么我们先只把主要边加入图中形成一棵树。
然后分析题目所给条件,总共切两次,一次切主要边,一次切附加边。要求把树切成两个部分,也就是说把某一个子树和原树分离。
如果我们选择切断点 x x x,点 y y y之间的主要边,那么一定要切断 x , y x,y x,y之间所有的附加边,才能真正的切断 x , y x,y x,y。
所以当一条附加边k连接点 x , y x,y x,y时,对于树上 x , y x,y x,y之间路径上的每一条主要边,如果我们要切某一条主要边,都要切掉k这一条边才能真正切断。 我们可以把他称为:“覆盖”。
由于只能切两次,所以我们对于主要边被覆盖的次数分情况讨论:
1、如果第一步把覆盖
0
0
0次的主要边切断,那么树就已经被切断了,所以第二步切任何的附加边都可以。
2、如果第一步把覆盖
1
1
1次的主要边切断,那么第二步只能切覆盖这条主要边的附加边才能把树切断。
3、如果第一步把覆盖超过
1
1
1次的主要边切断,那么第二次且那个都无法切断树。
上面三种把树切断的方案,可以通过加法原理得到方案数。
(加法原理:做一件事情,完成它有n类方式,第一类方式有M1种方法,第二类方式有M2种方法,……,第n类方式有Mn种方法,那么完成这件事情共有M1+M2+……+Mn种方法。)
那么如何知道当前的边被覆盖了多少次呢?我们发现每加入一条附加边(x,y)那么就会对树上x,y之间的所有边产生了影响,那么就对应了一个树上的区间加法问题,这样可以用树上差分解决。
具体如何实现呢?记住这个操作:
我们对于每一个点记录一个
v
a
l
val
val值,初始化为
0
0
0,每当加入一条附加边
(
x
,
y
)
(x,y)
(x,y)那么就
v
a
l
[
x
]
+
+
,
v
a
l
[
y
]
+
+
,
v
a
l
[
L
C
A
(
x
,
y
)
]
−
=
2
val[x]++,val[y]++,val[LCA(x,y)]-=2
val[x]++,val[y]++,val[LCA(x,y)]−=2,这样的话,在我们做好
v
a
l
val
val的加减之后对于整棵树
d
f
s
dfs
dfs一次,求出对于一个点
x
x
x的子树的
v
a
l
val
val值和
s
u
m
sum
sum,
s
u
m
sum
sum就是
x
x
x与
x
x
x的父节点
f
a
[
x
]
fa[x]
fa[x]之间这条主要边的被覆盖次数。
如图,蓝色的虚线边是附加边:
为什么这样子可以?因为之前提到一条一条附加边
(
x
,
y
)
(x,y)
(x,y)那么就会对树上
x
,
y
x,y
x,y之间的所有边产生了影响,如下图,黄色的边就是
(
x
,
y
)
(x,y)
(x,y)这条附加边影响到的主要边,黄色的边组成的路径是一定会经过
L
C
A
LCA
LCA的,但是一定不会经过
L
C
A
LCA
LCA上面的点,也就是说。
(
x
,
y
)
(x,y)
(x,y)只会影响到
L
C
A
LCA
LCA到
x
x
x,以及
L
C
A
LCA
LCA到
y
y
y的边,不会影响别的边。所以我们如果有一个在
L
C
A
LCA
LCA上面的点统计下来,为了不被影响,我们把
v
a
l
[
L
C
A
]
val[LCA]
val[LCA]的值
−
2
-2
−2。
超超超超详细了吧,全网最详细题解了吧呜呜呜。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e5+10;
struct edge
{
int x,y,c,next;
}a[N*2]; int len,last[N];
int dep[N],f[N][20];
void ins(int x,int y)
{
a[++len].x=x;a[len].y=y;
a[len].next=last[x];last[x]=len;
}
int bin[20];
void dfs(int x,int fa)//NlogN
{
dep[x]=dep[fa]+1;
f[x][0]=fa;
for(int i=1;bin[i]<=dep[x];i++)
f[x][i]=f[f[x][i-1]][i-1];
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y==fa) continue;
dfs(y,x);
}
}
int LCA(int x,int y)//logN
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--) //跳到一个深度
if(dep[x]-dep[y]>=bin[i])
x=f[x][i];
if(x==y) return x;
for(int i=20;i>=0;i--) //x,y一齐跳到LCA的孩子节点
if(dep[x]>=(1<<i) && f[x][i]!=f[y][i]) //如果f[x][i]=f[y][i]说明如果当前跳i会跳到LCA上面去
x=f[x][i],y=f[y][i];
return f[x][0];
}
int val[N];
int n,m;
void dfs2(int x,int fa)
{
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y==fa) continue;
dfs2(y,x);
val[x]+=val[y];
}
}
int main()
{
bin[0]=1;
for(int i=1;i<=20;i++) bin[i]=bin[i-1]*2;
scanf("%d%d",&n,&m);
len=0; memset(last,0,sizeof(last));
for(int i=1;i<n;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y); ins(y,x);
}
dfs(1,0);
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
int lca=LCA(x,y);
val[x]++,val[y]++,val[lca]-=2;
}
dfs2(1,0);
int ans=0;
for(int i=2;i<=n;i++)
{//加法原理
if(val[i]==0) ans+=m; //切断覆盖0次的主边,第二次可以且任意附加边
else if(val[i]==1) ans+=1; //切断覆盖1次的主边,第二次只能切覆盖他的附加边
//切覆盖次数>1次的主边无解
}
printf("%d\n",ans);
return 0;
}