POJ - 3417 Network 【树上差分 + LCA + 思维】

https://vjudge.net/problem/POJ-3417

 

题意:

先给一棵具有n个节点的树, 然后再给出m条边, 问从树上删去一条边, 再从m条边中删去一条边, 把这个图分成至少两部分的方案数.

 思路:

很容易就可以想到,每加入一条边,都会形成环。这条环是什么样的呢? 肯定是, u -> lca(u,v) -> v  - > u  肯定是这样一个环,我们可以把这条环上的路径的权值加一。路径仅限是原来 的树上的路径。当我们吧每个新加的边都弄完之后,

如果原来的树上的边的权值是 0 ,说明他很脆弱,把这条边去掉,就一定会形成两部分,所以另一条边就随便去掉, 有 m 中可能。

如果原来的树上的边的权值是 1 ,说明把这个边去掉,还要去掉一个特定的另外一条边,所以 只有一种可能。

如果边的权值大于1, 那在怎么去掉也不可能变成两部分。

所以最后我们只要统计边的权值就好了,在想一下,边的权值并不好加,我们我们选择让边的权值下降到他的下面节点上。

第一个点没有用。把下面的点的权值加1,把lca的那点 -2.  d[u]++, d[v]++ d[lca[u,v]] -= 2.

我们最后再树上跑dp,每个点的权值就是他的儿子的权值和。因为我们在lca - 2 了,所以不会影响到lca上面的点。

这个就像是 树状数组的在一个点加上一个值,在后边一个点减去一个值,结果不影响区间之外的求和。

扫描二维码关注公众号,回复: 2756378 查看本文章

有n 个点 n-1条边,n-1条边对应着n-1个点,所以根节点是没有用的。每次求lca ,lca这个点的作用就是防止v u 的值传给了 lca 的父亲节点。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <algorithm>
#define mem(x,v) memset(x,v,sizeof(x)) 
#define go(i,a,b)  for (int i = a; i <= b; i++)
#define og(i,a,b)  for (int i = a; i >= b; i--)
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
const int N = 2e6+10;
//vector<int>g[N];
struct node{
	int v,next;
}g[N];
int f[N][23],dep[N],dis[N],head[N];
int n,m,cnt = -1;
LL Ans = 0;

void Add(int u, int v){
	cnt++;
	g[cnt].next = head[u];
	head[u] = cnt;
	g[cnt].v = v;
	return;
}
void dfs(int u, int fa, int deep){
	f[u][0] = fa; dep[u] = deep;
	go(i,1,20) f[u][i] = f[f[u][i-1]][i-1];
	for (int i = head[u]; i != -1; i = g[i].next){
		if (g[i].v == fa) continue;
		dfs(g[i].v, u, deep+1);
	}
}
int lca(int x, int y){
	if (dep[x] < dep[y]) swap(x,y);
	og(i,19,0) if (dep[f[x][i]] >= dep[y]) x = f[x][i];
	if (x == y) return x;
	og(i,19,0) if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	return f[x][0];
}
LL dfss(int u, int fa){
	LL num = dis[u];
	for (int i = head[u]; i != -1; i = g[i].next){
		if (g[i].v == fa) continue;
		num += dfss(g[i].v,u);
	}
	if (num < 2 && u != 1){
		if (num == 1) Ans += 1; else Ans += m;
	}
	return num;
}
int main(){
	scanf("%d%d",&n,&m);
	mem(head,-1); 
	go(i,1,n-1){int x,y; scanf("%d%d",&x,&y); Add(x,y); Add(y,x);}//g[x].push_back(y); g[y].push_back(x);}
	dfs(1,0,1);
	go(i,1,m){
		int x,y;
		scanf("%d%d",&x, &y);
		// if (x == y) continue;
		dis[x]++; dis[y]++;
		dis[lca(x,y)] -= 2;
	}
	dfss(1,0);
    printf("%lld\n",Ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/81639379