洛谷 P10931 闇の連鎖

洛谷 P10931 闇の連鎖

题意

给定一棵 n 个点的树,有 m 条附加边。

第一次删除一条树边,第二次删除一条附加边。

求有多少种方案使原来的树不联通。

思路

考虑求出 fi 表示 i 的子树中有多少条附加边连向 i 的子树外。

fi=0,则把 ii 的父亲之间的树边删除后树已经不联通,随意删除一条附加边即可,对答案的贡献是 m

fi=1,则把 ii 的父亲之间的树边删除后,必须删除这条附加边才能满足题意,对答案的贡献是 1

fi2,则把 ii 的父亲之间的树边删除后,无论删除哪条附加边都不能满足题意,对答案的贡献是 0

现在想想如何求出 fi

对于每条附加边 (u,v)uv 对哪些点的 f 有贡献呢?

uLCA(u,v)(不含)的路径上,从 vLCA(u,v)(不含)的路径上的所有点的 f 都会加一。

为什么 LCA(u,v) 以上的点不会有贡献呢?

对于 LCA(u,v) 及其以上的点 xuv 都在 x 的子树中,不满足 f 的定义,即连向子树外的附加边,所以没有贡献。

那怎么快速计算贡献呢?

暴力更改 f 时间复杂度为 O(nm),不能通过。

树上路径问题可以想到使用树链剖分,不过太复杂。

可以使用树上差分快速统计。

uLCA(u,v) 路径上点权全部加一,等价于 u 到根节点路径上点权全部加一,LCA(u,v) 到根节点路径上点权全部减一。

可以把 fu 加一,fLCA(u,v) 减一,最后统计答案时遍历一遍把子树内的信息收上来。v 同理。

时间复杂度 O((n+m)logn)

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 5;
int tot, ver[N << 1], nxt[N << 1], head[N];
int fa[N][22], de[N];
int n, m, a[N], ans;
void add(int x, int y) {
	ver[++ tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
}
void DFS(int x) {
    de[x] = de[fa[x][0]] + 1;
    for (int i = 1; i <= 19; i ++)
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
    for (int i = head[x], y; i; i = nxt[i]) {
        y = ver[i];
        if (y == fa[x][0]) continue;
        fa[y][0] = x;
        DFS(y);
    }
}
inline int LCA(int x, int y) {
    if (de[x] < de[y]) swap(x, y);
    for (int i = 19; i >= 0; i --) 
        if (de[fa[x][i]] >= de[y]) x = fa[x][i];
    if (x == y) return x;
    for (int i = 19; i >= 0; i --)
        if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}
void dfs(int x, int fa) {
	for (int i = head[x], y; i; i = nxt[i]) {
		y = ver[i];
		if (y == fa) continue;
		dfs(y, x);
		a[x] += a[y]; // 把子树信息收上来
	}
}
signed main() {
	cin >> n >> m;
	for (int i = 1, x, y; i < n; i ++) {
		cin >> x >> y;
		add(x, y); add(y, x);
	}
	DFS(1);
	for (int i = 1, x, y; i <= m; i ++) {
		cin >> x >> y;
		a[x] ++, a[y] ++, a[LCA(x, y)] -= 2; // 差分
	}
	dfs(1, 0); // 收集信息
	for (int i = 2; i <= n; i ++) { // 统计答案
		if (!a[i]) {
			ans += m;
		} else if (a[i] == 1) {
			ans ++;
		}
	}
	cout << ans << "\n";
	return 0;
} 
posted @   maniubi  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示