poj3417 Network(树上差分)

版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/83045667

题意

给出一棵树,再给出几条附加边,使得树存在环。求割掉一条主要边和一条附加边能让树不连通的方案数。

题解

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;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/83045667