1. 程式人生 > >poj3417 Network 樹上差分+LCA

poj3417 Network 樹上差分+LCA

題目傳送門

  題目大意:給出一棵樹,再給出m條非樹邊,先割掉一條樹邊,再割掉一條非樹邊,問有幾種割法,使圖變成兩部分。

  思路:每一條 非樹邊會和一部分的樹邊形成一個環,分三種情況:

    對於那些沒有形成環的樹邊來說,割掉這條邊,就已經使圖分離,然後隨便割一條非樹邊就可以了,所以這樣的邊每次答案加上m。

    對於那些只存在在一個環中的樹邊來說,割掉這條邊,再割一條和他存在於同一個環中的那條非樹邊,也能合法,所以加一。

    對於存在於多個環中的樹邊,無論怎樣,都無法合法。

    也就是此時我們將題目轉化成了樹上的覆蓋問題,非樹邊的兩個端點覆蓋的樹上簡單路徑就是一個環,我們用樹上差分來解決這個問題,先處理出lca,f陣列表示每個點和父節點所在的邊的覆蓋次數。

    對於每一條非樹邊,端點是u,v,則f[ u ]和 f [ v ]加一,而u和v的公共祖先是沒有影響的,所以f[ lca (u,v) ]要減去2,抵消這個影響。每個節點除了要加上直接對這個點操作的值,還要加上來自子樹是否有覆蓋,所以dfs求得子樹的大小,此時 f 陣列則代表每個點的父邊所覆蓋的次數。

  

//#include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<queue>
#include
<iostream> #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; int n,m; const int maxn=100010; struct edges{ int to ,Next; }a[maxn<<1]; int head[maxn],tot,fa[maxn][23],deg[maxn]; ll f[maxn]; inline void init(){ CLR(head,-1),tot=0; CLR(f,0); } inline void
addv(int u,int v){ a[++tot]={v,head[u]}; head[u]=tot; } inline void bfs(){ queue<int >q; deg[1]=0; fa[1][0]=1; q.push(1); while(!q.empty()){ int tmp=q.front(); q.pop(); for(int i=1;i<21;i++) { fa[tmp][i]= fa[fa[tmp][i-1]][i-1]; } for(int i=head[tmp];i!=-1;i=a[i].Next) { int v=a[i].to; if(v==fa[tmp][0])continue; deg[v]=deg[tmp]+1; fa[v][0]=tmp; q.push(v); } } } inline int lca(int u,int v){ if(deg[u] > deg[v])swap(u,v); int hu=deg[u],hv=deg[v]; int tu=u,tv=v; for(int det=hv-hu,i=0;det;det>>=1,i++) if(det&1) tv=fa[tv][i]; if(tu==tv)return tu; for(int i=20;i>=0;i--){ if(fa[tu][i]==fa[tv][i]) continue; tu=fa[tu][i]; tv=fa[tv][i]; } return fa[tu][0]; } inline void dfs(int u){ for(int i=head[u];i!=-1;i=a[i].Next) { int v=a[i].to; if(v!=fa[u][0]){ dfs(v); f[u]+=f[v]; } } } int main(){ init(); cin>>n>>m; for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); addv(u,v); addv(v,u); } bfs(); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); f[u]++; f[v]++; f[lca(u,v)]-=2; // printf("lca %d\n",lca(u,v)); } dfs(1); ll ans=0; for(int i=2;i<=n;i++) { // cout<<i<<" "<<f[i]<<endl; if(f[i]==0){ ans+=m; }else if(f[i]==1){ ans++; } } cout<<ans<<endl; }
View Code Network
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 6736   Accepted: 1916

Description

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

Source

POJ Monthly--2007.10.06, Yang Mu