1. 程式人生 > >poj3417 Network(樹上差分)

poj3417 Network(樹上差分)

題意

給出一棵樹,再給出幾條附加邊,使得樹存在環。求割掉一條主要邊和一條附加邊能讓樹不連通的方案數。

題解

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