SP4155 OTOCI - OTOCI

题意

给定一棵树,每个点有一个点权,一开始整棵树没有连任何边,接着 mm 次操作,要求询问两点是否联通,修改点的权值和询问两个结点之间所有点权的和。

解法

本题和 P4312 完全相同,因为我还不会 LCT,又因为没有删边操作,考虑并查集加树剖离线维护。

注意并不是所有 bridge 都要求建边,所以输入时也要用到并查集。

本题有一个坑点,就是输入的整张图最后不一定是树而可能是森林,所以树剖时要 dfs 多次。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;

#define int long long

const int N = 1e6 + 5;

int a[N], na[N], n, m;

class SegmentTree
{
public:
	struct Node
	{
		int l, r, sum;
	}tr[N << 2];
	void push_up(int u)
	{
		tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
	}
	void build(int u, int l, int r)
	{
		tr[u] = { l, r, 0 };
		if (l == r) tr[u].sum = na[r];
		else
		{
			int mid = l + r >> 1;
			build(u << 1, l, mid);
			build(u << 1 | 1, mid + 1, r);
			push_up(u);
		}
	}
	void update(int u, int x, int v)
	{
		if (tr[u].l == x and tr[u].r == x) tr[u].sum = v;
		else
		{
			int mid = tr[u].l + tr[u].r >> 1;
			if (x <= mid) update(u << 1, x, v);
			else update(u << 1 | 1, x, v);
			push_up(u);
		}
	}
	int query(int u, int l, int r)
	{
		if (tr[u].l >= l and tr[u].r <= r) return tr[u].sum;
		int mid = tr[u].l + tr[u].r >> 1, res = 0;
		if (l <= mid) res = query(u << 1, l, r);
		if (r > mid) res += query(u << 1 | 1, l, r);
		return res;
	}
};

struct Node
{
	string s;
	int u, v;
}q[N];

vector<int> G[N];
int f[N];

class TreeCut
{
public:
	int dep[N], id[N], fa[N], son[N], sz[N], top[N], cnt;
	void dfs1(int u, int father, int depth)
	{
		dep[u] = depth;
		fa[u] = father;
		sz[u] = 1;
		for (int i = 0; i < G[u].size(); i++)
		{
			int nx = G[u][i];
			if (nx == father) continue;
			dfs1(nx, u, depth + 1);
			sz[u] += sz[nx];
			if (sz[son[u]] < sz[nx]) son[u] = nx;
		}
	}
	void dfs2(int u, int tf)
	{
		top[u] = tf;
		id[u] = ++cnt;
		na[cnt] = a[u];
		if (!son[u]) return;
		dfs2(son[u], tf);
		for (int i = 0; i < G[u].size(); i++)
		{
			int nx = G[u][i];
			if (nx == fa[u] || nx == son[u]) continue;
			dfs2(nx, nx);
		}
	}
	SegmentTree seg;
	void build()
	{
		for (int i = 1; i <= n; i++)
		{
			if (!sz[i]) dfs1(i, i, 1);
		}
		for (int i = 1; i <= n; i++)
		{
			if (!top[i]) dfs2(i, i);
		} 
		seg.build(1, 1, n);
	}
	void update(int u, int p)
	{
		seg.update(1, id[u], p);
	}
	int query_path(int u, int v)
	{
		int res = 0;
		while (top[u] ^ top[v])
		{
			if (dep[top[u]] < dep[top[v]]) swap(u, v);
			res += seg.query(1, id[top[u]], id[u]);
			u = fa[top[u]];
		}
		if (dep[u] < dep[v]) swap(u, v);
		res += seg.query(1, id[v], id[u]);
		return res;
	}
};

TreeCut tc;

class DSU
{
public:
	void Init()
	{
		for (int i = 0; i < N; i++)
		{
			f[i] = i;
		}
	}
	int find(int x)
	{
		return (f[x] == x ? x : f[x] = find(f[x]));
	}
	void join(int x, int y)
	{
		f[find(x)] = find(y);
	}
	bool judge(int x, int y)
	{
		return find(x) == find(y);
	}
};

DSU ds;

signed main()
{
	scanf("%lld", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%lld", &a[i]);
	}
	scanf("%lld", &m);
	ds.Init(); 
	for (int i = 1; i <= m; i++)
	{
		cin >> q[i].s >> q[i].u >> q[i].v;
		if (q[i].s == "bridge")
		{
			if (ds.judge(q[i].u, q[i].v)) continue;
			ds.join(q[i].u, q[i].v);
			G[q[i].u].push_back(q[i].v);
			G[q[i].v].push_back(q[i].u);
		}
	}
	tc.build();
	ds.Init();
	for (int i = 1; i <= m; i++)
	{
		if (q[i].s == "bridge")
		{
			bool f = ds.judge(q[i].u, q[i].v);
			printf("%s\n", f ? "no" : "yes");
			if (!f) ds.join(q[i].u, q[i].v);
		}
		else if (q[i].s == "penguins")
		{
			tc.update(q[i].u, q[i].v);
		}
		else if (q[i].s == "excursion")
		{
			if (!ds.judge(q[i].u, q[i].v)) printf("impossible\n");
			else printf("%lld\n", tc.query_path(q[i].u, q[i].v));
		}
	}
	return 0;
}
posted @   HappyBobb  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示