1. 程式人生 > >POJ3417 Network(LCA+樹上差分)

POJ3417 Network(LCA+樹上差分)

傳送門:http://poj.org/problem?id=3417

Network

Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 6047   Accepted: 1724

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

題意:給n個點,給m條附加邊,接下來是n-1對點,組成樹,接下來m條是附加邊。現在想要把樹搞成不連通的兩部分,一開始選一條主要邊弄斷,然後在選擇切一個附加邊。問有幾種方案。

思路:加一條附加邊到樹上會成環,比如在a,b上加了附加邊,那麼假如切的是ab路上的某個邊,那麼第二步肯定切斷ab這條附加邊才能變成不連通兩部分。也就是說ab路上每個邊都被ab這個附加邊覆蓋了一次。如果第一步我們把覆蓋0次的樹上主要邊去掉,那麼第二步幹掉任意附加邊都行,如果幹掉的是覆蓋次數為1的,那麼只有一個方案,覆蓋2次及以上的沒有辦法了。ps:標記覆蓋次數可以使用樹上差分演算法。設個權值陣列val[],讓附加邊兩端的ab的權值+1,lca(a,b)的權值-2。最後跑一邊dfs求出ff[i]表示以i為根的樹各節點權值之和,這個ff[i]就是i和他父親之間這條邊被覆蓋的次數。

程式碼:

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
#include<map>
#include<vector>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;
const ll maxv=1e5+5;

ll head[maxv],vis[maxv],d[maxv],fa[maxv][20],val[maxv],ff[maxv];
ll n,m,edgenum,ans;

struct node
{
	ll to,next;
}edge[maxv<<1];

void init()
{
	fill(d,d+maxv,0);
	fill(head,head+maxv,-1);
	fill(ff,ff+maxv,0);
	fill(val,val+maxv,0);
	edgenum=0;
}

void addedge(ll u,ll v)
{
	edge[edgenum].to=v;
	edge[edgenum].next=head[u];
	head[u]=edgenum++;
}

void bfs()
{
	queue<ll>q;
	q.push(1);
	d[1]=1;
	while(!q.empty())
	{
		ll now=q.front();
		q.pop();
		for(ll i=head[now];i!=-1;i=edge[i].next)
		{
			ll y=edge[i].to;
			if(d[y])
				continue;
			d[y]=d[now]+1;
			fa[y][0]=now;
			for(ll j=1;j<20;j++)
			{
				fa[y][j]=fa[fa[y][j-1]][j-1];
			}
			q.push(y);
		}
	}
}

ll lca(ll x,ll y)
{
	if(d[x]>d[y])
		swap(x,y);
	for(ll i=19;i>=0;i--)
	{
		if(d[fa[y][i]]>=d[x])
			y=fa[y][i];
	}
	if(x==y)
		return x;
	for(ll i=19;i>=0;i--)
	{
		if(fa[x][i]!=fa[y][i])
			x=fa[x][i],y=fa[y][i];
	}
	return fa[x][0];
}

ll dfs(ll p,ll father)
{
	ll sum=val[p];
	for(ll i=head[p];i!=-1;i=edge[i].next)
	{
		ll now=edge[i].to;
		if(now==father)
			continue;
		ll cnt=dfs(now,p);
		sum+=cnt;
	}
	ff[p]=sum;
	return sum;
}

int main()
{
	while(scanf("%lld%lld",&n,&m)!=EOF)
	{
		init();
		ll u,v;
		for(ll i=1;i<=n-1;i++)
		{
			scanf("%lld%lld",&u,&v);
			addedge(u,v);
			addedge(v,u);
		}
		bfs();
		for(int i=1;i<=m;i++)
		{
			ll a,b;
			scanf("%lld%lld",&a,&b);
			val[a]++;
			val[b]++;
			val[lca(a,b)]-=2;
		}
		dfs(1,-1);
		ans=0;
		for(ll i=2;i<=n;i++)
		{
			if(ff[i]==1)
			{
				ans++;
			}
			else if(ff[i]==0)
			{
				ans+=m;
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}