POJ3417 Network(LCA+树上差分)

版权声明:Amove? https://blog.csdn.net/Amovement/article/details/87512577

                                               Network

Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 7308   Accepted: 2074

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

一、原题地址

点我传送

二、大致题意

给出n个点  和 n-1条双向边。在原图上增加m条新边。

在原图中去除1条边,并且在新增的边里去除1条。现在询问有多少种方式能把图分成两部分。

三、大致思路

1、每一条新增的边会导致原图中出现一个环,如果我们断去这条边,再在这个环上断去一条边,那么这就是一种我们需要的答案了。

2、对于没有构成环的边,我们断去它立马就可以把图分成两部分,此时在就有m种方案(随便断去新增的一条边)。

3、如果一条边在两个环(或多个环)中,那么无论我们怎么操作都是无用的。

所以问题就转化为了让我们统计每一条边,增加的环数量。对于每次新增的边(x,y),我们找到他们的最近公共祖先lca ,那么x到lca再到y的这条路上的所有边都会增加1的环。

此时采取差分的想法,记录每个点的贡献cnt[ i ],最后做一次DFS答案累加起来就可以了。

四、代码

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
using namespace std;
const int inf = (1 << 30) - 1;
const long long int INF = 1e18;
typedef long long LL;


int n,m;
const int maxn = 100005;
const int DEG = 20;
struct Edge
{
	int to, next;
} edge[maxn << 2];

int head[maxn], tot;
LL cnt[maxn];
LL ans;
void addEdge(int u, int v)
{
	edge[tot].to = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}

int fa[maxn][DEG], dep[maxn];

void BFS(int root)
{
	queue<int> que;
	dep[root] = 0;
	fa[root][0] = root;
	que.push(root);
	while (!que.empty())
	{
		int tmp = que.front();
		que.pop();
		for (int i = 1; i < DEG; i++) fa[tmp][i] = fa[fa[tmp][i - 1]][i - 1];
		for (int i = head[tmp]; i != -1; i = edge[i].next)
		{
			int v = edge[i].to;
			//cout << v << endl;
			if (v == fa[tmp][0]) continue;
			dep[v] = dep[tmp] + 1;
			fa[v][0] = tmp;
			que.push(v);
		}
	}
}

int LCA(int u, int v)
{
	if (dep[u] > dep[v]) swap(u, v);
	int hu = dep[u], hv = dep[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 = DEG - 1; i >= 0; i--)
	{
		if (fa[tu][i] == fa[tv][i])
		{
			continue;
		}
		tu = fa[tu][i];
		tv = fa[tv][i];
	}
	return fa[tu][0];
}

void read()
{
    for(int i=1;i<=n-1;i++)
    {
        int u,v;
        scanf("%d %d",&u,&v);
        addEdge(u,v);
        addEdge(v,u);
    }
}

LL DFS(int nx,int pre)
{
    LL ret=cnt[nx];
    for(int i=head[nx];i!=-1;i=edge[i].next)
    {
        int to =edge[i].to;
        if(to!=pre)
        {
            LL val=DFS(to,nx);
            if(val==0)ans+=(LL)m;
            else if(val==1)ans++;
            ret+=(LL)val;
        }
    }
    return ret;
}

void init()
{
	tot = 0;
	for(int i=1;i<=n;i++)head[i]=-1,cnt[i]=0;
}

void work()
{
    init();
    read();
    BFS(1);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        int lca=LCA(x,y);
        cnt[x]++;cnt[y]++;
        cnt[lca]-=2;
    }
    ans=0;
    DFS(1,-1);
    printf("%lld\n",ans);
}

int main()
{
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        work();
    }
}

猜你喜欢

转载自blog.csdn.net/Amovement/article/details/87512577