P5163 WD与地图

发布时间 2023-10-09 17:21:29作者: Ender_32k

Day \(\lfloor\pi^3\rfloor\)

原神答辩缝合怪题目。

先考虑无向图的版本怎么做。套路地,考虑时间倒流,然后就变成了加边、改点权、查询连通块前 \(k\) 大之和,线段树合并加并查集维护即可。

现在的边有向,依旧考虑时间倒流,相当于将连通块改成了强连通分量。问题在于只有一条边不一定能使两条边处在同一个强连通分量内,这启示我们对边考虑。

如果能求出每条边 \(e_i(u,v)\)\((u,v)\) 首次被加入同一个强连通块内的时刻 \(t_{i}\),那么 \(u,v\) 就相当于连了无向边,这条边的存在时间区间为 \([t_i,q]\),于是就可以转换为无向图的版本。

压力来到如何求出 \(t_{i}\),显然越早加入的边的 \(t_i\) 越小,那么整体二分即可,每次分治答案区间 \([l,r]\),则加入插入时间在 \([l,\text{mid}]\) 的所有边,跑一遍 tarjan,再递归右半边,撤销后递归左半边即可。复杂度两个 \(\log\),注意不能遍历到孤立点否则就会爆炸。

// Problem: P5163 WD与地图
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5163
// Memory Limit: 500 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define mt make_tuple
#define mp make_pair
#define fi first
#define se second

using namespace std;
typedef long long ll;
typedef pair<int, int> pi;
typedef tuple<int, int, int> tu;
bool Mbe;

const int N = 1e5 + 100;
const int M = 2e5 + 200;
const int W = 1e9;

int n, m, q, w[N], id[N], rt[N];
int dfc, top, dfn[N], low[N], in[N], st[N];
struct node { int op, x, y; } qr[M];
struct edge { int u, v, t, ti; } e[M], el[M], er[M];
map<pi, int> eid;
vector<int> g[N], p;
vector<ll> ans;

namespace DSU {
	int fa[N], sz[N];
	vector<int> op;
	int gf(int x) { return x == fa[x] ? x : gf(fa[x]); }
	int gff(int x) { return x == fa[x] ? x : fa[x] = gf(fa[x]); }
	void mrg(int x, int y) {
		if ((x = gf(x)) == (y = gf(y))) return;
		if (sz[x] > sz[y]) swap(x, y);
		fa[x] = y, sz[y] += sz[x], op.pb(x);
	}
	void del(int tp) {
		while ((int)op.size() > tp) {
			int x = op.back(); op.pop_back();
			sz[fa[x]] -= sz[x], fa[x] = x;
		}
	}
	void init(int n) { 
		for (int i = 1; i <= n; i++) fa[i] = i, sz[i] = 1; 
		op.clear(); 
	}
}
using DSU::gf;
using DSU::gff;
using DSU::mrg;
using DSU::del;
using DSU::init;

namespace SGT {
	int tot, lc[N << 6], rc[N << 6], ct[N << 6];
	ll s[N << 6];
	#define ls lc[x]
	#define rs rc[x]
	void upd(int l, int r, int p, int c, int &x) {
		if (!x) x = ++tot;
		ct[x] += c, s[x] += 1ll * c * p;
		if (l == r) return;
		int mid = (l + r) >> 1;
		if (p <= mid) upd(l, mid, p, c, ls);
		else upd(mid + 1, r, p, c, rs);
	}
	void mrg(int l, int r, int &x, int y) {
		if (!x || !y) return (x = x | y), void();
		s[x] += s[y], ct[x] += ct[y];
		if (l == r) return;
		int mid = (l + r) >> 1;
		mrg(l, mid, ls, lc[y]), mrg(mid + 1, r, rs, rc[y]);
	}
	ll qry(int l, int r, int k, int x) {
		if (!x) return 0;
		if (ct[x] <= k) return s[x];
		if (l == r) return 1ll * k * l;
		int mid = (l + r) >> 1;
		if (ct[rs] >= k) return qry(mid + 1, r, k, rs);
		return s[rs] + qry(l, mid, k - ct[rs], ls);
	}
}
using SGT::upd;
using SGT::mrg;
using SGT::qry;

void clr() {
	for (int i : p)
		g[i].clear(), dfn[i] = low[i] = in[i] = 0;
	p.clear(), dfc = top = 0;
}

void dfs(int u) {
	dfn[u] = low[u] = ++dfc, st[++top] = u;
	for (int v : g[u]) {
		if (!dfn[v]) dfs(v), low[u] = min(low[u], low[v]);
		else if (!in[v]) low[u] = min(low[u], dfn[v]);
	}
	if (low[u] != dfn[u]) return;
	while (st[top] != u) 
		in[st[top]] = u, mrg(u, st[top]), top--;
	in[u] = u, top--;
}

void conq(int l, int r, int s, int t) {
	if (l > r || s == t) {
		for (int i = l; i <= r; i++) e[i].ti = s;
		return;
	}
	int mid = (s + t) >> 1, tp = DSU::op.size();
	for (int i = l; i <= r; i++) {
		int u = gf(e[i].u), v = gf(e[i].v);
		if ((u ^ v) && e[i].t > mid) 
			g[u].pb(v), p.pb(u), p.pb(v);
	}
	for (int i : p) 
		if (!dfn[i]) dfs(i); 
	clr();
	int cl = 0, cr = 0;
	for (int i = l; i <= r; i++) {
		int u = gf(e[i].u), v = gf(e[i].v);
		if ((u == v) && e[i].t > mid) er[++cr] = e[i];
		else el[++cl] = e[i];
	}
	for (int i = l; i <= l + cl - 1; i++) e[i] = el[i - l + 1];
	for (int i = l + cl; i <= l + cl + cr - 1; i++) e[i] = er[i - l - cl + 1];
	conq(l, l + cl - 1, s, mid), del(tp), conq(l + cl, l + cl + cr - 1, mid + 1, t);
}

void solve() {
	cin >> n >> m >> q;
	for (int i = 1; i <= n; i++) cin >> w[i];
	for (int i = 1, u, v; i <= m; i++) 
		cin >> u >> v, eid[mp(u, v)] = i, e[i] = (edge) { u, v, q };
	for (int i = 1, op, x, y; i <= q; i++) {
		cin >> op >> x >> y, qr[i] = (node) { op, x, y };
		if (op == 1) e[eid[mp(x, y)]].t = i - 1;
		else if (op == 2) w[x] += y;
	}
	sort(e + 1, e + m + 1, [](edge &x, edge &y) { 
		return x.t < y.t; 
	}), init(n), conq(1, m, 0, q);
	sort(e + 1, e + m + 1, [](edge &x, edge &y) {
		return x.ti < y.ti;
	}), init(n);
	for (int i = 1; i <= n; i++) upd(0, W, w[i], 1, rt[i]);
	for (int i = q, j = m; i; i--) {
		int op = qr[i].op, x = qr[i].x, y = qr[i].y;
		while (j && e[j].ti >= i) {
			int u = gff(e[j].u), v = gff(e[j].v);
			if (u ^ v) DSU::fa[v] = u, mrg(0, W, rt[u], rt[v]);
			j--;
		}
		if (op == 2) x = gff(x), upd(0, W, w[qr[i].x], -1, rt[x]), upd(0, W, w[qr[i].x] -= y, 1, rt[x]);
		else if (op == 3) x = gff(x), ans.pb(qry(0, W, y, rt[x]));
	}
    reverse(ans.begin(), ans.end());
    for (ll i : ans) cout << i << '\n';
}

bool Med;
int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cerr << (&Mbe - &Med) / 1048576.0 << " MB\n";
	#ifdef FILE
		freopen("1.in", "r", stdin);
		freopen("1.out", "w", stdout);
	#endif
	int T = 1;
	// cin >> T;
	while (T--) solve();
	cerr << (int)(1e3 * clock() / CLOCKS_PER_SEC) << " ms\n";
	return 0;
}