loj #10131

抽离题意
求删除一条树边和一条非树边后将图分成不连通的两部分的方案数
对于一棵树,再加入一条边就会产生环。若只有一个环,说明只加入了一条非树边 (x, y),记 lca 为 l, 那么

对于任意一条 (x, l) and (y, l) 上的树边,都会产生 1 的贡献,也就是说若一条树边可以产生 1 的贡献,

那么该边必须只存在于一个环中,这样的话,对于每条非树边,在树上 (x, l) and (y, l) 的边 +1,这样找出

权值为 1 的边,对答案的贡献为 1 ,当然如果某条边不存在于任意环,那么它的贡献为 m。
树上差分即可

#include <bits/stdc++.h>

const int N = 1e5 + 10;

std:: vector <int> Vec[N];
int n, m;
int f[N][27];
int deep[N];

void Dfs(int u, int f_, int dep) {
	int S = Vec[u].size();
	deep[u] = dep;
	for(int i = 0; i < S; i ++) {
		int v = Vec[u][i];
		if(v == f_) continue;
		f[v][0] = u;
		Dfs(v, u, dep + 1);
	}
}

void Pre() {
	for(int j = 1; j <= 25; j ++)
		for(int i = 1; i <= n; i ++)
			f[i][j] = f[f[i][j - 1]][j - 1];
}

inline int Lca(int x, int y) {
	if(deep[x] < deep[y]) std:: swap(x, y);
	int del = deep[x] - deep[y];
	for(int i = 0; (1 << i) <= del; i ++) if(del & (1 << i)) x = f[x][i];
	if(x == y) return x;
	for(int i = 24; i >= 0; i --) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	return f[x][0];
}

int Cnt[N];

void Dfs_ans(int u, int f_) {
	int S = Vec[u].size();
	for(int i = 0; i < S; i ++) {
		int v = Vec[u][i];
		if(v == f_) continue;
		Dfs_ans(v, u);
		Cnt[u] += Cnt[v];
	}
}

int main() {
	std:: cin >> n >> m;
	for(int i = 1; i < n; i ++) {
		int u, v; std:: cin >> u >> v;
		Vec[u].push_back(v), Vec[v].push_back(u);
	}
	Dfs(1, 0, 1);
	Pre();
	for(int i = 1; i <= m; i ++) {
		int x, y;
		std:: cin >> x >> y;
		Cnt[x] ++, Cnt[y] ++, Cnt[Lca(x, y)] -= 2;
	}
	Dfs_ans(1, 0);
	int Answer = 0;
	for(int i = 2; i <= n; i ++) {
		if(Cnt[i] == 0) Answer += m;
		else if(Cnt[i] == 1) Answer ++;
	}
	std:: cout << Answer;
	return 0;
}
posted @ 2018-09-19 09:07  xayata  阅读(142)  评论(0编辑  收藏  举报