zoukankan      html  css  js  c++  java
  • WD与地图

    博客园链接

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

    • 删除一条有向边

    • 修改点权

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

    (n,m,q le 2 imes 10^5)

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

    现在有向边与无向边不同的是,每条边加入后不一定会将 (u_i,v_i) 缩成一个 SCC。于是一种想法是,对于每条边,我们二分其两端点被缩成一个 SCC 的时间,用 tarjan 判定,然后再像无向图那样搞。复杂度:(O(m^2log 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
    ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    Browsersync 浏览器自动刷新
    react学习历程问题记载(二)
    react学习历程问题记载(一)
    LessJs笔记
    toFixed的使用
    react+ts封装AntdUI的日期选择框之月份选择器DatePicker.month
    elementUI实现日期框选中项文本高亮
    react+lib-flexible适配浏览器宽度配置
    vue+lib-flexible实现大小屏幕,超大屏幕的适配展示。
    div+伪元素实现太极图
  • 原文地址:https://www.cnblogs.com/JiaZP/p/13785818.html
Copyright © 2011-2022 走看看