题意
给出一棵树,再给出几条附加边,使得树存在环。求割掉一条主要边和一条附加边能让树不连通的方案数。
题解
lca+树上差分
观察题目中附加边的特点,连接(x,y)的附加边使得(x,y)之间的连接方式又增加了1。
不妨设一开始的连接方式为0。一条附加边可以使(x,y)之间的主要边的连接方式+1。
如果割一条边被加到了2或以上,说明割掉这条主要边之后还得再割两条附加边,无法做到。
如果只被加到了1,说明在割掉这条主要边之后把那条使这条边+1的附加边割掉,可以把图分成两部分。ans+=1
如果没有被加过,那么在割掉这条主要边之后图就已经不连通了,再随意割一条附加边即可。ans+=m
对于树上一段连续的边的加减操作,用树上差分即可。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+10,maxm=2e5+10;
int bin[30];
int n,m;
struct E{int y,next;}e[maxn*2];int len=0,last[maxn];
void ins(int x,int y)
{
e[++len]=(E){y,last[x]};last[x]=len;
}
int f[maxn][30];int dep[maxn];
void dfs(int x,int fa)
{
for(int k=last[x];k;k=e[k].next)
{
int y=e[k].y;
if(y==fa) continue;
dep[y]=dep[x]+1;
f[y][0]=x;
for(int i=1;i<=20;i++) f[y][i]=f[f[y][i-1]][i-1];
dfs(y,x);
}
}
int lca(int x,int y)
{
if(dep[y]>dep[x]) swap(x,y);
for(int i=20;i>=0;i--)
if(dep[y]<=dep[x]-bin[i]) x=f[x][i];//debug Ìøx
if(x==y) return x;
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 d[maxn];int ans=0;
void dfs2(int x,int fa)
{
for(int k=last[x];k;k=e[k].next)
{
int y=e[k].y;
if(y==fa) continue;
dfs2(y,x);
d[x]+=d[y];
}
if(x==1) return ;
if(d[x]==0) ans+=m;
else if(d[x]==1) ans++;
}
int main()
{
bin[0]=1;for(int i=1;i<=20;i++) bin[i]=1<<i;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
dep[1]=0;dfs(1,0);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
d[x]++;d[y]++;d[lca(x,y)]-=2;//树上差分
}
dfs2(1,0);//还原实际值并统计方案
printf("%d\n",ans);
return 0;
}