树剖30pts求调
查看原帖
树剖30pts求调
481621
Zhang_Wenjie楼主2024/10/20 10:37

思路:倒序处理,因为保证联通,我就先把操作离线下来,构造一颗末状态下的树(子集,末状态也可能是图),对于树,显然所有边都是关键路径。

若末状态是图,那剩下的边连在树上形成环,环上所有边都不是关键路径,等价于 u <-> v 路径边权赋值 0

操作同理

#include <bits/stdc++.h>
#define re register int 
#define lp p << 1
#define rp p << 1 | 1

using namespace std;
const int N = 3e4 + 10, M = 1e5 + 10;

struct Edge
{
	int to, w, next;
}e[M << 1];
int idx, h[N];
struct Tree
{
	int l, r, sum, tag;
}t[N << 2];
int n, m, id[N], cnt, a[N], mat_a[N];
int fa[N], son[N], dep[N], siz[N], top[N];

struct Get_edge
{
	int x, y;
}g[M];
bool vis[M];
struct Query
{
	int c, x, y;
}q[M];
int num;
map<pair<int, int>, bool> mp;
vector<int> ans;

int f[N];

int find(int x)
{
	return f[x] == x ? x : f[x] = find(f[x]);
}

inline void add(int x, int y, int w)
{
	e[++ idx] = (Edge){y, w, h[x]};
	h[x] = idx;
}

void dfs(int u, int fu)
{
	fa[u] = fu;
	dep[u] = dep[fu] + 1;
	siz[u] = 1;
	
	for (re i = h[u]; i; i = e[i].next)
	{
		int v = e[i].to, w = e[i].w;
		if (v == fu) continue;
		a[v] = w;
		dfs(v, u);
		siz[u] += siz[v];
		if (siz[son[u]] < siz[v]) son[u] = v;
	}
}

void mark(int u, int t)
{
	top[u] = t;
	id[u] = ++ cnt;
	mat_a[id[u]] = a[u];
	if (son[u]) mark(son[u], t);
	
	for (re i = h[u]; i; i = e[i].next)
	{
		int v = e[i].to;
		if (v == fa[u] || v == son[u]) continue;
		mark(v, v);
	}
}

inline void push_up(int p)
{
	t[p].sum = t[lp].sum + t[rp].sum;
}

inline void push_down(int p)
{
	if (t[p].tag != -1)
	{
		t[lp].sum = t[rp].sum = t[p].tag;
		t[lp].tag = t[rp].tag = t[p].tag;
		t[p].tag = -1;
	} 
}

inline void build(int p, int l, int r)
{
	t[p].l = l, t[p].r = r;
	t[p].tag = -1;
	if (l == r)
	{
		t[p].sum = mat_a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(lp, l, mid);
	build(rp, mid + 1, r);
	push_up(p);
}

inline void update(int p, int l, int r, int k)
{
	if (l <= t[p].l && t[p].r <= r)
	{
		t[p].sum = k;
		t[p].tag = k;
		return;
	}
	push_down(p);
	int mid = (t[p].l + t[p].r) >> 1;
	if (l <= mid) update(lp, l, r, k);
	if (r > mid) update(rp, l, r, k);
	push_up(p);
}

inline int query(int p, int l, int r)
{
	if (l <= t[p].l && t[p].r <= r) return t[p].sum;
	push_down(p);
	int res = 0;
	int mid = (t[p].l + t[p].r) >> 1;
	if (l <= mid) res += query(lp, l, r);
	if (r > mid) res += query(rp, l, r);
	
	return res;
}

inline void update_path(int u, int v, int k)
{
	if (u == v) return;
	while (top[u] != top[v])
	{
		if (dep[top[u]] < dep[top[v]]) swap(u, v);
		update(1, id[top[u]], id[u], k);
		u = fa[top[u]];
	}
	if (dep[u] < dep[v]) swap(u, v);
	update(1, id[v] + 1, id[u], k);
}

inline int query_path(int u, int v)
{
	if (u == v) return 0;
	int res = 0;
	while (top[u] != top[v]) 
	{
		if (dep[top[u]] < dep[top[v]]) swap(u, v);
		res += query(1, id[top[u]], id[u]);
		u = fa[top[u]];
	}
	if (dep[u] < dep[v]) swap(u, v);
	res += query(1, id[v] + 1, id[u]);
	
	return res;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
//	freopen("lane9.in", "r", stdin);
//	freopen("lane9.ans", "w", stdout);
	
	cin >> n >> m;
	for (re i = 1; i <= m; i ++) cin >> g[i].x >> g[i].y;
	while (1)
	{
		int op; cin >> op;
		if (op == -1) break;
		else 
		{
			int x, y; cin >> x >> y;
			q[++ num] = (Query){op, x, y};
			mp[{min(x, y), max(x, y)}] = true;
		}
	}
	for (re i = 1; i <= n; i ++) f[i] = i;
	for (re i = 1; i <= m; i ++)
	{
		int x = g[i].x, y = g[i].y;
		if (!mp[{min(x, y), max(x, y)}] && find(x) != find(y))
		{
			f[find(x)] = find(y);
			add(x, y, 1);
			add(y, x, 1); 
			vis[i] = true;
//			cout << x << " --- " << y << '\n';
		}
	}
	dfs(1, 0); mark(1, 1);
	build(1, 1, n);
	for (re i = 1; i <= m; i ++) // 末状态图子集树构造好后,剩下没有连的边且不在操作集合中 
	{
		int x = g[i].x, y = g[i].y;
		if (!mp[{min(x, y), max(x, y)}] && !vis[i]) 
		{
			update_path(x, y, 0);
//			cout << x << ' ' << y << '\n';
		}
	}
	for (re i = num; i >= 1; i --)
	{
		int c = q[i].c, x = q[i].x, y = q[i].y;
		if (c)
			ans.push_back(query_path(x, y));
		else 
			update_path(x, y, 0);
	}
	for (re i = ans.size() - 1; i >= 0; i --) cout << ans[i] << '\n';
	
	return 0;
}
2024/10/20 10:37
加载中...