1. 程式人生 > >POJ3417Network(LCA+樹上查分||樹剖+線段樹)

POJ3417Network(LCA+樹上查分||樹剖+線段樹)

兩個 += gpo scan channels span work received best

Yixght is a manager of the company called SzqNetwork(SN). Now she‘s very worried because she has just received a bad news which denotes that DxtNetwork(DN), the SN‘s business rival, intents to attack the network of SN. More unfortunately, the original network of SN is so weak that we can just treat it as a tree. Formally, there are N

nodes in SN‘s network, N-1 bidirectional channels to connect the nodes, and there always exists a route from any node to another. In order to protect the network from the attack, Yixght builds M new bidirectional channels between some of the nodes.

As the DN‘s best hacker, you can exactly destory two channels, one in the original network and the other among the M

new channels. Now your higher-up wants to know how many ways you can divide the network of SN into at least two parts.

Input

The first line of the input file contains two integers: N (1 ≤ N ≤ 100 000), M (1 ≤ M ≤ 100 000) — the number of the nodes and the number of the new channels.

Following N

-1 lines represent the channels in the original network of SN, each pair (a,b) denote that there is a channel between node a and node b.

Following M lines represent the new channels in the network, each pair (a,b) denote that a new channel between node a and node b is added to the network of SN.

Output

Output a single integer — the number of ways to divide the network into at least two parts.

Sample Input

4 1
1 2
2 3
1 4
3 4

Sample Output

3

題意:

一棵樹,後來加了m條新邊,形成了一個有環無向圖,問刪去原樹上一條邊和m條新邊中的一條,使得圖不連通。這樣的方案有多少。

思路:

以前看到過,完全沒有思路,放棄了。今天又看到了,還是沒有思路。。。GG

把原圖dfs建立成有根樹,假設1是根節點,再想,有方向可能就有思路了,oh,還是沒有。。。GG

  • 試著手動在兩點之間加一條新邊,我們看到形成了一個環。
  • 對於環上加的一條新邊,兩點間每一條原樹邊都多進入了一個環。
  • 1, 如果一條邊進入了兩個環,則不用再考慮它。
  • 2, 如果只進入一個環,則它與對應的新邊是一組答案。
  • 3, 如果一條表沒有進入任何環,那它與任何一條新邊是一組答案。

  • 對每一條新邊e,其端點為u,v,LCA(u,v)=a,則把路徑u-a-v上每一條邊加1,最後處理即可。
  • 顯然可以用線段樹+lazy做。數據上感覺可以過。但是學到了樹上差分。。。像前綴和一樣。感覺很簡單,但是沒想到。。。

:sum[u]+1;sum[v]+1;sum[a]-2; 由葉子向根root累加即可。

註意:對象是邊,而不是頂點的sum[]。

我寫的是樹剖(因為在練習樹剖),註意找LCA的時候註意 if(dpt[u]<dpt[v]) 是刷新。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
#define swap(a,b) {a^=b;b^=a;a^=b;}
using namespace std;
const int maxn=400010;
int Laxt[maxn],Next[maxn],To[maxn],cnt;
int fa[maxn],son[maxn],size[maxn],dpt[maxn],top[maxn];
int n,m,ans,sum[maxn];
void add(int u,int v)
{
    Next[++cnt]=Laxt[u];
    Laxt[u]=cnt; To[cnt]=v;
}
void dfs1(int u,int pre)
{
    fa[u]=pre;dpt[u]=dpt[pre]+1;size[u]=1;son[u]=0;
    for(int i=Laxt[u];i;i=Next[i]){
        int v=To[i]; if(v==pre) continue;
        dfs1(v,u);size[u]+=size[v];
        if(!son[u]||size[v]>size[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int Top)
{
    top[u]=Top; if(!son[u]) return ; dfs2(son[u],Top);
    for(int i=Laxt[u];i;i=Next[i])
        if(To[i]!=fa[u]&&To[i]!=son[u]) dfs2(To[i],To[i]);
}

int dfs3(int u,int pre)
{
    for(int i=Laxt[u];i;i=Next[i]){
        int v=To[i];
        if(v!=pre){
            int tmp=dfs3(v,u);    
            sum[u]+=tmp; 
            if(tmp==0) ans+=m;
            if(tmp==1) ans+=1;
        }
    }
    return sum[u];
}
int find(int u,int v)
{
    int a=top[u],b=top[v];
    while(a!=b){
        if(dpt[a]<dpt[b]) { swap(a,b);swap(u,v);}
        u=fa[a]; a=top[u];
    }
    if(dpt[u]<dpt[v]) return u; return v;
}
int main()
{

    while(~scanf("%d%d",&n,&m)){
        memset(Laxt,0,sizeof(Laxt)); cnt=0;
        memset(sum,0,sizeof(sum)); ans=0;
        for(int i=1;i<n;i++){
            int v,u; scanf("%d%d",&u,&v);
            add(u,v);add(v,u);
        }
        dfs1(1,0);
        dfs2(1,1);
        for(int i=1;i<=m;i++){
            int u,v; scanf("%d%d",&u,&v);
            int anc=find(u,v);
            sum[u]++;sum[v]++;sum[anc]-=2;
        }
        dfs3(1,0);
        printf("%d\n",ans);
    } return 0;
}

POJ3417Network(LCA+樹上查分||樹剖+線段樹)