WD与地图

博客园链接

\(n\) 个点, \(m\) 条边的有向图,每个点有初始点权,要求支持:

  • 删除一条有向边

  • 修改点权

  • 求点 \(x\) 所在强连通分量的前 \(k\) 大点权和。

\(n,m,q \le 2 \times 10^5\)

如果是无向图就好说很多了,时光倒流后并查集维护连通块及权值线段树合并即可。

现在有向边与无向边不同的是,每条边加入后不一定会将 \(u_i,v_i\) 缩成一个 SCC。于是一种想法是,对于每条边,我们二分其两端点被缩成一个 SCC 的时间,用 tarjan 判定,然后再像无向图那样搞。复杂度:\(O(m^2\log q)\),不如暴力。

显然要整体二分。但是每次需要加入 \(0...mid\) 的所有边后 tarjan,复杂度爆炸。这个也好说,我们有可撤销并查集,这样我们就可以让 \(mid\) 任意游动,而只花费大概游动距离那么多的时间。然而可撤销并查集只支持 \(mid\) 左移,如何处理 \(mid\) 右移的复杂度?事实上,我们只需要加 \(ql...qr\) 这么多边。因为这个整体二分比较神奇,其询问和修改是一个东西:询问肯定是时间 \(l\) 之前加入缩不成 SCC 的边,以及时间 \(l...mid\) 中新出现的边,而我们需要加的也恰好是这些。因为之前我们已经用并查集维护缩了一波 SCC,那些“缩不成 SCC 的边”一定被留下来,其余边一定没用。而一层中每条边只会出现一次,因此 tarjan 的复杂度为 \(O(nlogn)\),加上可撤销并查集的维护复杂度,总复杂度为 \(O(nlog^2n)\)

整体二分完以后就转化成无向图问题做即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#define N 401000
#define NN 20001000
typedef long long ll;
template<typename T> inline void read(T &x) {
	x = 0; char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
}
using namespace std;
const int up = 1e9;
template<typename T> inline void MIN(T &a, T b) {
	if (b < a)	a = b;
}
template<typename T> inline void MAX(T &a, T b) {
	if (a < b)	a = b;
}
int n, m, q;
int a[N];
struct Edge{
	int u, v, t, fti;//first connected time
}E[N], tl[N], tr[N];
map<int, int> mp[N];
struct Qy {
	int opt, x, y;
}qy[N];
inline bool cmp(const Edge &a, const Edge &b) {
	return a.fti < b.fti;
}

int fath[N], siz[N];
struct Info {
	int x, y;
	Info(int xx = 0, int yy = 0) { x = xx, y = yy; }
}info[N << 1];
int top;
int find(int cur) {
	return fath[cur] == cur ? cur : find(fath[cur]);
}
inline void merge(int x, int y) {
	x = find(x), y = find(y);
	if (siz[x] > siz[y])	swap(x, y);
	info[++top] = (Info){x, y};
	siz[y] += siz[x]; fath[x] = y;
}
inline void del(int tp) {
	int x = info[tp].x, y = info[tp].y;
	siz[y] -= siz[x]; fath[x] = x;
}

struct edge{
	int nxt, to;
}e[N << 1];
int head[N], ecnt;
inline void addedge(int from, int to) {
	e[++ecnt] = (edge){head[from], to};
	head[from] = ecnt;
}
int dfn[N], low[N], dcnt;
int stk[N], stop;
bool instk[N];
void tarjan(int cur) {
	dfn[cur] = low[cur] = ++dcnt;
	stk[++stop] = cur; instk[cur] = true;
	for (int i = head[cur]; i; i = e[i].nxt) {
		int to = e[i].to;
		if (!dfn[to])	tarjan(to), MIN(low[cur], low[to]);
		else if (instk[to])	MIN(low[cur], dfn[to]);
	}
	if (low[cur] == dfn[cur]) {
		int lst = 0, tmp;
		do {
			tmp = stk[stop--];
			if (!lst)	lst = tmp;
			else	merge(tmp, lst);
			instk[tmp] = false;
		} while (tmp != cur);
	}
}
int htot, h[N << 1];
inline void clear() {
	ecnt = dcnt = stop = 0;
	for (int i = 1; i <= htot; ++i) {
		int p = h[i];
		dfn[p] = low[p] = head[p] = 0;
		instk[p] = false;
	}
	htot = 0;
}


void sol(int L, int R, int ql, int qr) {
	if (L == R) {
		for (int i = ql; i <= qr; ++i)	E[i].fti = L;
		return ;
	}
	int mid = (L + R) >> 1;
	for (int i = ql; i <= qr; ++i) if (E[i].t <= mid) {
		int fu = find(E[i].u), fv = find(E[i].v);
		h[++htot] = fu, h[++htot] = fv;
		if (fu != fv) addedge(fu, fv);
	}
	int tmp = top;
	for (int i= 1; i <= htot; ++i)	if (!dfn[h[i]]) tarjan(h[i]);
	int nl = 0, nr = 0;
	for (int i = ql; i <= qr; ++i) {
		int u = find(E[i].u), v = find(E[i].v);
		if (E[i].t <= mid && u == v)	tl[++nl] = E[i];
		else	tr[++nr] = E[i];
	}
	for (int i = 1; i <= nl; ++i)	E[ql + i - 1] =  tl[i];
	for (int i = 1; i <= nr; ++i)	E[ql + nl + i - 1] = tr[i];
	clear();
	sol(mid + 1, R, ql + nl, qr);
	while (top > tmp)	del(top), --top;
	sol(L, mid, ql, ql + nl - 1);
}

int ls[NN], rs[NN], tsiz[NN], rt[N], ttot;
ll sum[NN], ans[N];
inline void pushup(int cur) {
	tsiz[cur] = tsiz[ls[cur]] + tsiz[rs[cur]];
	sum[cur] = sum[ls[cur]] + sum[rs[cur]];
}
void add(int L, int R, int pos, int x, int &cur) {
	if (!cur)	cur = ++ttot;
	if (L == R)	return tsiz[cur] += x, sum[cur] += L * x, void();
	int mid = (L + R) >> 1;
	if (pos <= mid)	add(L, mid, pos, x, ls[cur]);
	else	add(mid + 1, R, pos, x, rs[cur]);
	pushup(cur);
}
ll query(int L, int R, int k, int cur) {
	if (!cur)	return 0;
	if (L == R)	return 1ll * k * L;
	int mid = (L + R) >> 1;
	int sz = tsiz[rs[cur]];
	if (k <= sz)	return query(mid + 1, R, k, rs[cur]);//bug
	k -= sz; return sum[rs[cur]] + query(L, mid, k, ls[cur]);//bug
}
int SGT_Merge(int L, int R, int x, int y) {
	if (!x || !y)	return x ^ y;
	if (L == R)	return sum[x] += sum[y], tsiz[x] += tsiz[y], x;
	int mid = (L + R) >> 1;
	ls[x] = SGT_Merge(L, mid, ls[x], ls[y]);
	rs[x] = SGT_Merge(mid + 1, R, rs[x], rs[y]);
	pushup(x);
	return x;
}
inline void Merge(int x, int y) {
	x = find(x), y = find(y); if (x == y)	return ;
	if (siz[x] > siz[y])	swap(x, y);
	rt[y] = SGT_Merge(1, up, rt[y], rt[x]);
	merge(x, y);
}

signed main() {
	read(n), read(m), read(q);
	for (int i = 1; i <= n; ++i)	read(a[i]), fath[i] = i, siz[i] = 1;
	for (int i = 1; i <= m; ++i)
		read(E[i].u), read(E[i].v), E[i].t = 0, mp[E[i].u][E[i].v] = i;
	for (int i = 1; i <= q; ++i) {
		int opt, x, y; read(opt), read(x), read(y);
		qy[i] = (Qy){opt, x, y};
		if (opt == 1)	E[mp[x][y]].t = q - i + 1;
		if (opt == 2)	a[x] += y;//bug
	}
	sol(0, q + 1, 1, m);
	for (int i = 1; i <= n; ++i)	add(1, up, a[i], 1, rt[i]);
	sort(E + 1, E + 1 + m, cmp);
	int ptr = 1;
	for (int i = q; i; --i) {
		while (ptr <= m && E[ptr].fti <= q - i + 1)	Merge(E[ptr].u, E[ptr].v), ++ptr;
		int opt = qy[i].opt, x = qy[i].x, y = qy[i].y;
		if (opt == 2) {
			int fx = find(x);
			add(1, up, a[x], -1, rt[fx]);
			a[x] -= y;
			add(1, up, a[x], 1, rt[fx]);
		}
		if (opt == 3)	{
			x = find(x);
			if (tsiz[rt[x]] <= y)	ans[i] = sum[rt[x]];
			else	ans[i] = query(1, up, y, rt[x]);
		}
	}
	for (int i = 1; i <= q; ++i)	if (qy[i].opt == 3)	printf("%lld\n", ans[i]);
	return 0;
}
posted @ 2020-10-09 15:00  JiaZP  阅读(236)  评论(0编辑  收藏  举报