https://loj.ac/problem/10131
题目描述
给出一张图,有(n)个节点和两类边,一类为主要边,一类为次要边,主要边构成图的一棵生成树,求有多少种方案可以在断掉一条主要边和一条次要边后与不再连通。
思路
我们把图看做一棵树,加上一些非树边。我们考虑如果存在一条(x、y)的非树边,那么树上(x、y)路径上的点一定要斩断这条非树边。所以对于每条非树边((x,y)),我们记为把树上(x、y)的路径覆盖了一次,由此我们可以知道,对于覆盖了(0)次的边,我们斩断它后选任意一条非树边都可以;对于覆盖了(1)次的点,我们只有一种方案;对于覆盖了(2)次及以上的点,我们无法实现不连通。
而覆盖了几次这个内容我们可以用树上差分实现,由于边不好记录,我们先转化为点,对于((x,y)),使用树上差分,我们把(val[x]++,val[y]++,val[lca]-2)实现,最后每个点的实际值就是其子树的权值和。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int nxt[N<<1],to[N<<1],from[N<<1],tot,head[N];
void add_edge(int x,int y)
{
nxt[++tot]=head[x];
head[x]=tot;
to[tot]=y;from[tot]=x;
}
int f[N][22],dep[N];
void deal_first(int u,int fa)
{
dep[u]=dep[fa]+1;
for(int i=0;i<20;i++)
f[u][i+1]=f[f[u][i]][i];
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==fa)continue ;
f[v][0]=u;
deal_first(v,u);
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=20;i>=0;i--)
{
if(dep[f[x][i]]>=dep[y])x=f[x][i];
if(x==y)return y;
}
for(int i=20;i>=0;i--)
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
return f[x][0];
}
int sum[N],val[N];
void dfs(int u,int fa)
{
sum[u]=val[u];
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==fa)continue ;
dfs(v,u);
sum[u]+=sum[v];
}
}
int read()
{
int res=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
return res*w;
}
void write(int x)
{
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
void writeln(int x)
{
write(x);
putchar('
');
}
int main()
{
int n=read(),m=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add_edge(x,y);add_edge(y,x);
}
deal_first(1,0);
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
int lca=LCA(x,y);
val[x]++;val[y]++;
val[lca]-=2;
}
dfs(1,0);
int ans=0;
for(int i=2;i<=n;i++)
if(sum[i]==1)ans++;
else if(sum[i]==0)ans+=m;
writeln(ans);
return 0;
}