题解【LOJ3145】「APIO2019」桥梁

题面

讲一下部分分:

  • 子任务 1:直接每次暴力做即可。
  • 子任务 2:一条链,线段树单点修改 + 维护区间最小值,加上二分。
  • 子任务 3:不会。
  • 子任务 4:没有修改,维护原图的一棵 Kruskal 重构树即可。
  • 子任务 5:不会。

接下来讲正解。

发现这个数据范围正好可以根号跑过,于是想到分块。

将操作分块,并将每一块中的边分成在块中不会被修改和会被修改的两类。

前一类边可以直接按边权从大到小加入,用并查集维护连通性和 \(size\)

后一类边我们可以对于每一次询问暴力扫,看是否可以被加入,整个过程还需要使用可撤销并查集。

具体实现细节见代码。

#include <bits/stdc++.h>
#define DEBUG fprintf(stderr, "Passing [%s] line %d\n", __FUNCTION__, __LINE__)
#define File(x) freopen(x".in","r",stdin); freopen(x".out","w",stdout)

using namespace std;

typedef long long LL;
typedef pair <int, int> PII;
typedef pair <int, PII> PIII;

template <typename T>
inline T gi()
{
	T f = 1, x = 0; char c = getchar();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return f * x;
}

const int INF = 0x3f3f3f3f, N = 100003, M = N << 1;
const int SZ = 500;

int n, m, q;
int tot;
int fa[N], sz[N];
int nid[N]; //old id
int val[N]; //edge's value
int ans[N];
struct UnionFind {int u, v;} stk[N]; int tp; //a stack of dsu
struct Node {int u, v, w, id;} a[M], p[M], g[M], tmp[M];
struct Query {int ty, x, y, id;} o[M], t1[M], t2[M];
bool vis[M]; //is modify?

inline bool cmp(Node x, Node y) {return x.w == y.w ? x.id < y.id : x.w > y.w;}
inline bool cmp1(Query x, Query y) {return x.y == y.y ? x.id < y.id : x.y > y.y;}

int getf(int u) {return fa[u] == u ? u : getf(fa[u]);}
inline void unionn(int u, int v)
{
	int fu = getf(u), fv = getf(v);
	if (fu == fv) return;
	if (sz[fu] > sz[fv]) swap(fu, fv);
	stk[++tp] = {fu, fv};
	fa[fu] = fv, sz[fv] += sz[fu];
}

inline void Back() {int u = stk[tp].u, v = stk[tp].v; sz[v] -= sz[u], fa[u] = u, --tp;}

inline void solve()
{
	int tp1 = 0, tp2 = 0; tp = 0;
	for (int i = 1; i <= m; i+=1) vis[i] = false, nid[a[i].id] = i;
	for (int i = 1; i <= n; i+=1) sz[i] = 1, fa[i] = i;
	for (int i = 1; i <= tot; i+=1)
		if (o[i].ty == 1) vis[o[i].x] = true, t1[++tp1] = o[i];
		else t2[++tp2] = o[i];
	sort(t2 + 1, t2 + 1 + tp2, cmp1);
	int now = 1;
	for (int i = 1; i <= tp2; i+=1)
	{
		while (now <= m && a[now].w >= t2[i].y)
		{
			if (!vis[a[now].id]) unionn(a[now].u, a[now].v);
			++now;
		}
		int nowtp = tp;
		for (int j = 1; j <= tp1; j+=1) val[t1[j].x] = a[nid[t1[j].x]].w;
		for (int j = 1; j <= tp1; j+=1)
			if (t1[j].id < t2[i].id) val[t1[j].x] = t1[j].y;
		for (int j = 1; j <= tp1; j+=1)
			if (val[t1[j].x] >= t2[i].y)
				unionn(a[nid[t1[j].x]].u, a[nid[t1[j].x]].v);
		ans[t2[i].id] = sz[getf(t2[i].x)];
		while (tp > nowtp) Back();
	}
	for (int i = 1; i <= tp1; i+=1) a[nid[t1[i].x]].w = t1[i].y;
	int nl = 0, nr = 0;
	for (int i = 1; i <= m; i+=1)
		if (vis[a[i].id]) p[++nl] = a[i];
		else g[++nr] = a[i];
	sort(p + 1, p + 1 + nl, cmp);
	int i = 1, j = 1, cnt = 0;
	while (i <= nl && j <= nr)
		if (p[i].w >= g[j].w) a[++cnt] = p[i++];
		else a[++cnt] = g[j++];
	while (i <= nl) a[++cnt] = p[i++];
	while (j <= nr) a[++cnt] = g[j++];
	return;
}

int main()
{
	n = gi <int> (), m = gi <int> ();
	for (int i = 1; i <= m; i+=1)
		a[i].u = gi <int> (), a[i].v = gi <int> (), a[i].w = gi <int> (), a[i].id = i;
	sort(a + 1, a + 1 + m, cmp);
	q = gi <int> ();
	for (int i = 1; i <= q; i+=1)
	{
		++tot;
		o[tot].ty = gi <int> (), o[tot].x = gi <int> (), o[tot].y = gi <int> (), o[tot].id = i;
		if (tot == SZ) solve(), tot = 0;
	}
	if (tot) solve();
	for (int i = 1; i <= q; i+=1) if (ans[i]) printf("%d\n", ans[i]);
	return 0;
}
posted @ 2020-12-04 10:47  csxsi  阅读(59)  评论(0编辑  收藏  举报