P5163 WD与地图()

P5163 WD与地图

比较套路的题,先把删边变为加边,考虑每一条边正真有用的时间,那么便是它属于某一个强连通分量的最早时间。如何计算呢?考虑整体二分,分治一个时间\(mid\),把时间\(\le mid\)的边加入图,跑\(tarjan\),对边按是否属于强连通分量分为两部分往下做,为了保证时间复杂度,可以利用并查集将强连通分量缩成一个点,这样在\(O(n \log n)\)的时间下我们就求出了每一条边真正有用的时间。
接下来就简单了,按时间加边,维护每个联通块的权值线段树,线段树合并即可。

#include<cstdio>
#include<map>
#include<algorithm>
#include<iostream>
#define LL long long
#define IN inline
using namespace std;
const int N = 5e5 + 5;
int n, m, Q, st[N], ed[N], cnt; LL a[N];

struct qy{int opt, x, y;}b[N];
IN int read() {
	int t = 0,res = 0; char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) t |= (ch == '-');
	for (; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ 48);
	return t ? -res : res;
}
map<int, bool> mp[N];
int dfn[N], low[N], dfc, top, stk[N], vis[N], cor[N], colt, tot, bz[N], h[N], fa[N];
struct nd{int x, y, z, t;}f[N], c[N], X[N];
struct edge{int to, nxt;}e[N << 1];
void add(int x, int y){e[++tot] = edge{y, h[x]}, h[x] = tot;}
void tarjan(int u) {
	dfn[u] = low[u] = ++dfc, stk[++top] = u, vis[u] = 1;
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if (!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
		else (vis[v]) && (low[u] = min(low[u], dfn[v]));
	}
	if (dfn[u] == low[u]) {
		colt++, cor[u] = colt, vis[u] = 0;
		for (; stk[top] != u; top--) cor[stk[top]] = colt, vis[stk[top]] = 0;
		top--;
	}
}
int find(int x){if (fa[x] != x) fa[x] = find(fa[x]);return fa[x];}
void merge(int x, int y){int t1 = find(x), t2 = find(y); (t1 ^ t2) && (fa[t1] = t2);}
void clear(int x){dfn[x] = h[x] = vis[x] = cor[x] = 0;}
void solve(int l, int r, int L, int R) {
	if (L > R) return;
	if (l == r) {
		for (int i = L; i <= R; i++) c[i].t = l;
		return;
	}
	int mid = l + r >> 1, cx = 0, tx = 0;
	for (int i = L; i <= R; i++)
		if (c[i].z <= mid) add(find(c[i].x), find(c[i].y)), f[++cx] = nd{find(c[i].x), find(c[i].y), i, 0};
	for (int i = 1; i <= cx; i++) if (!dfn[f[i].x]) tarjan(f[i].x);
	for (int i = 1; i <= cx; i++)
		if (cor[f[i].x] == cor[f[i].y] && cor[f[i].x]) X[++tx] = c[f[i].z], bz[f[i].z] = 1;
	for (int i = 1; i <= cx; i++) clear(f[i].x), clear(f[i].y); tot = dfc = top = colt = 0;
	int midx = tx;
	for (int i = L; i <= R; i++) if (!bz[i]) X[++tx] = c[i];
	for (int i = L; i <= R; i++) c[i] = X[i - L + 1], bz[i] = 0;
	solve(l, mid, L, L + midx - 1);
	for (int i = 1; i <= midx; i++) merge(X[i].x, X[i].y);
	solve(mid + 1, r, L + midx, R);
}

int siz, sum[N * 12], ls[N * 12], rs[N * 12], at, rt[N]; LL g[N * 12], ans[N], A[N];
void merge_seg(int &p1, int p2, int l, int r){
	if (!p1 || !p2) return p1 = p1 + p2, void();
	sum[p1] += sum[p2], g[p1] += g[p2];
	if (l == r) return; int mid = l + r >> 1;
	merge_seg(ls[p1], ls[p2], l, mid), merge_seg(rs[p1], rs[p2], mid + 1, r);
}
void merge_tree(int x, int y) {
	int t1 = find(x), t2 = find(y);
	if (t1 ^ t2) fa[t1] = t2, merge_seg(rt[t2], rt[t1], 1, at);
}
void update(int &p, int l, int r, int u, int v) {
	if (!p) p = ++siz;
	sum[p] += v, g[p] += (LL)v * A[u];
	if (l == r) return; int mid = l + r >> 1;
	if (u <= mid) update(ls[p], l, mid, u, v);
	else update(rs[p], mid + 1, r, u, v);
}
LL query(int p, int l, int r, int k) {
	if (!p) return 0; 
	if (l == r) return min(g[p], (LL)k * A[l]); int mid = l + r >> 1;
	if (sum[rs[p]] >= k) return query(rs[p], mid + 1, r, k);
	return g[rs[p]] + query(ls[p], l, mid, k - sum[rs[p]]);
}
bool cmp(nd x, nd y){return x.t < y.t;}
int getlow(LL x){return lower_bound(A + 1, A + 1 + at, x) - A;}
int main() {
	n = read(), m = read(), Q = read();

	for (int i = 1; i <= n; i++) a[i] = read(), A[++at] = a[i];
	for (int i = 1; i <= m; i++) st[i] = read(), ed[i] = read();
	for (int i = 1; i <= Q; i++) {
		b[i] = qy{read(), read(), read()};
		if (b[i].opt == 1) c[++cnt] = nd{b[i].x, b[i].y, Q - i + 1, 0}, mp[b[i].x][b[i].y] = 1;
		if (b[i].opt == 2) a[b[i].x] += b[i].y, A[++at] = a[b[i].x];
	}
	sort(A + 1, A + 1 + at), at = unique(A + 1, A + 1 + at) - A - 1;
	
	for (int i = 1; i <= m; i++)
		if (!mp[st[i]][ed[i]]) c[++cnt] = nd{st[i], ed[i], 0, 0};
	for (int i = 1; i <= n; i++) fa[i] = i;
	solve(0, Q + 1, 1, cnt), sort(c + 1, c + 1 + cnt, cmp);
	
	for (int i = 1; i <= n; i++) fa[i] = i, update(rt[i], 1, at, getlow(a[i]), 1);
	int tans = 0;
	for (int i = Q, j = 1; i >= 1; i--) {
		while (j <= cnt && c[j].t <= Q - i + 1) merge_tree(c[j].x, c[j].y), j++;
		if (b[i].opt == 1) continue;
		int rot = rt[find(b[i].x)];
		if (b[i].opt == 2)
			update(rot, 1, at, getlow(a[b[i].x]), -1), a[b[i].x] -= b[i].y, update(rot, 1, at, getlow(a[b[i].x]), 1);
		if (b[i].opt == 3) ans[++tans] = query(rot, 1, at, b[i].y);
	}
	for (int i = tans; i >= 1; i--) printf("%lld\n", ans[i]);
}

————————

P5163 WD与地图

比较套路的题,先把删边变为加边,考虑每一条边正真有用的时间,那么便是它属于某一个强连通分量的最早时间。如何计算呢?考虑整体二分,分治一个时间\(mid\),把时间\(\le mid\)的边加入图,跑\(tarjan\),对边按是否属于强连通分量分为两部分往下做,为了保证时间复杂度,可以利用并查集将强连通分量缩成一个点,这样在\(O(n \log n)\)的时间下我们就求出了每一条边真正有用的时间。
接下来就简单了,按时间加边,维护每个联通块的权值线段树,线段树合并即可。

#include<cstdio>
#include<map>
#include<algorithm>
#include<iostream>
#define LL long long
#define IN inline
using namespace std;
const int N = 5e5 + 5;
int n, m, Q, st[N], ed[N], cnt; LL a[N];

struct qy{int opt, x, y;}b[N];
IN int read() {
	int t = 0,res = 0; char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) t |= (ch == '-');
	for (; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ 48);
	return t ? -res : res;
}
map<int, bool> mp[N];
int dfn[N], low[N], dfc, top, stk[N], vis[N], cor[N], colt, tot, bz[N], h[N], fa[N];
struct nd{int x, y, z, t;}f[N], c[N], X[N];
struct edge{int to, nxt;}e[N << 1];
void add(int x, int y){e[++tot] = edge{y, h[x]}, h[x] = tot;}
void tarjan(int u) {
	dfn[u] = low[u] = ++dfc, stk[++top] = u, vis[u] = 1;
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if (!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
		else (vis[v]) && (low[u] = min(low[u], dfn[v]));
	}
	if (dfn[u] == low[u]) {
		colt++, cor[u] = colt, vis[u] = 0;
		for (; stk[top] != u; top--) cor[stk[top]] = colt, vis[stk[top]] = 0;
		top--;
	}
}
int find(int x){if (fa[x] != x) fa[x] = find(fa[x]);return fa[x];}
void merge(int x, int y){int t1 = find(x), t2 = find(y); (t1 ^ t2) && (fa[t1] = t2);}
void clear(int x){dfn[x] = h[x] = vis[x] = cor[x] = 0;}
void solve(int l, int r, int L, int R) {
	if (L > R) return;
	if (l == r) {
		for (int i = L; i <= R; i++) c[i].t = l;
		return;
	}
	int mid = l + r >> 1, cx = 0, tx = 0;
	for (int i = L; i <= R; i++)
		if (c[i].z <= mid) add(find(c[i].x), find(c[i].y)), f[++cx] = nd{find(c[i].x), find(c[i].y), i, 0};
	for (int i = 1; i <= cx; i++) if (!dfn[f[i].x]) tarjan(f[i].x);
	for (int i = 1; i <= cx; i++)
		if (cor[f[i].x] == cor[f[i].y] && cor[f[i].x]) X[++tx] = c[f[i].z], bz[f[i].z] = 1;
	for (int i = 1; i <= cx; i++) clear(f[i].x), clear(f[i].y); tot = dfc = top = colt = 0;
	int midx = tx;
	for (int i = L; i <= R; i++) if (!bz[i]) X[++tx] = c[i];
	for (int i = L; i <= R; i++) c[i] = X[i - L + 1], bz[i] = 0;
	solve(l, mid, L, L + midx - 1);
	for (int i = 1; i <= midx; i++) merge(X[i].x, X[i].y);
	solve(mid + 1, r, L + midx, R);
}

int siz, sum[N * 12], ls[N * 12], rs[N * 12], at, rt[N]; LL g[N * 12], ans[N], A[N];
void merge_seg(int &p1, int p2, int l, int r){
	if (!p1 || !p2) return p1 = p1 + p2, void();
	sum[p1] += sum[p2], g[p1] += g[p2];
	if (l == r) return; int mid = l + r >> 1;
	merge_seg(ls[p1], ls[p2], l, mid), merge_seg(rs[p1], rs[p2], mid + 1, r);
}
void merge_tree(int x, int y) {
	int t1 = find(x), t2 = find(y);
	if (t1 ^ t2) fa[t1] = t2, merge_seg(rt[t2], rt[t1], 1, at);
}
void update(int &p, int l, int r, int u, int v) {
	if (!p) p = ++siz;
	sum[p] += v, g[p] += (LL)v * A[u];
	if (l == r) return; int mid = l + r >> 1;
	if (u <= mid) update(ls[p], l, mid, u, v);
	else update(rs[p], mid + 1, r, u, v);
}
LL query(int p, int l, int r, int k) {
	if (!p) return 0; 
	if (l == r) return min(g[p], (LL)k * A[l]); int mid = l + r >> 1;
	if (sum[rs[p]] >= k) return query(rs[p], mid + 1, r, k);
	return g[rs[p]] + query(ls[p], l, mid, k - sum[rs[p]]);
}
bool cmp(nd x, nd y){return x.t < y.t;}
int getlow(LL x){return lower_bound(A + 1, A + 1 + at, x) - A;}
int main() {
	n = read(), m = read(), Q = read();

	for (int i = 1; i <= n; i++) a[i] = read(), A[++at] = a[i];
	for (int i = 1; i <= m; i++) st[i] = read(), ed[i] = read();
	for (int i = 1; i <= Q; i++) {
		b[i] = qy{read(), read(), read()};
		if (b[i].opt == 1) c[++cnt] = nd{b[i].x, b[i].y, Q - i + 1, 0}, mp[b[i].x][b[i].y] = 1;
		if (b[i].opt == 2) a[b[i].x] += b[i].y, A[++at] = a[b[i].x];
	}
	sort(A + 1, A + 1 + at), at = unique(A + 1, A + 1 + at) - A - 1;
	
	for (int i = 1; i <= m; i++)
		if (!mp[st[i]][ed[i]]) c[++cnt] = nd{st[i], ed[i], 0, 0};
	for (int i = 1; i <= n; i++) fa[i] = i;
	solve(0, Q + 1, 1, cnt), sort(c + 1, c + 1 + cnt, cmp);
	
	for (int i = 1; i <= n; i++) fa[i] = i, update(rt[i], 1, at, getlow(a[i]), 1);
	int tans = 0;
	for (int i = Q, j = 1; i >= 1; i--) {
		while (j <= cnt && c[j].t <= Q - i + 1) merge_tree(c[j].x, c[j].y), j++;
		if (b[i].opt == 1) continue;
		int rot = rt[find(b[i].x)];
		if (b[i].opt == 2)
			update(rot, 1, at, getlow(a[b[i].x]), -1), a[b[i].x] -= b[i].y, update(rot, 1, at, getlow(a[b[i].x]), 1);
		if (b[i].opt == 3) ans[++tans] = query(rot, 1, at, b[i].y);
	}
	for (int i = tans; i >= 1; i--) printf("%lld\n", ans[i]);
}