WD与地图题解

Posted 忘怀星的博客小屋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WD与地图题解相关的知识,希望对你有一定的参考价值。

神仙题……有整体二分,线段树合并qwq。

首先要将删边改为加边,倒叙处理操作。

我们要使用整体二分处理一条边是在什么时候才让边的两端在同一个强连通分量里。对于每一次solve,我们传入二分的两端和包含需要处理的边的 vector 数组。将在 mid 前出现的边建出一张图,然后在上面跑一遍 tarjan 。然后将 vector 内已经在同一个强连通分量里并且出现早于 mid 的边丢到左区间,其余丢到右区间进行处理。

每次跑 tarjan 的时候进行缩点,将在一个强连通分量内的点使用可撤销并查集维护到一起。即相当于一个强连通分量使用他们的祖先来替代。跑完 tarjan 后将并查集保留,图删除。容易发现丢到左区间处理的边进行缩点之后其实只是一个点集。所以并不会出现漏掉强连通分量的问题。

处理完这些后与询问一起排序处理,遇到边则并查集连边,当然这里是不必须使用可撤销并查集的,并且合并并查集的同时要进行线段树合并,来维护保存了并查集内元素的权值线段树,使用动态开点。查询的时候使用线段树上二分即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <queue>
#define int long long
using namespace std;

int read()
{
	int a = 0,x = 1;char ch = getchar();
	while(ch > \'9\' || ch < \'0\') {if(ch == \'-\') x = -1;ch = getchar();}
	while(ch >= \'0\' && ch <= \'9\') {a = a*10 + ch-\'0\';ch = getchar();}
	return a*x;
}
const int N=1e6+7;
int n,m,q,s[N],ans[N];
struct edge{
	int u,v,tim;
}E[N];
struct quer{
	int op,a,b,tim;
}Q[N],cal[N];
map<pair<int,int>,int>mp;
struct DSU{
	int fa[N],siz[N],top,stk[N];
	void init(int n) {for(int i = 1;i <= n;i ++) fa[i] = i,siz[i] = 1;}
	int find(int s) {return fa[s] == s ? s : find(fa[s]);}
	void merge(int a,int b)
	{
		a = find(a),b = find(b);
		if(a == b) return ;
		if(siz[a] > siz[b]) swap(a,b);
		fa[a] = b,siz[b] += siz[a];
		stk[++top] = a;
	}
	void del(int cur)
	{
		while(top > cur) {
			siz[fa[stk[top]]] -= siz[stk[top]];
			fa[stk[top]] = stk[top];--top;
		}
	}
}uni;
struct TARJAN{
	int head[N],go[N],nxt[N],cnt;
	int low[N],dfn[N],ins[N],stk[N],top;
	vector<int>point;
	void add(int u,int v) {
		if(!head[u]) point.push_back(u);
		point.push_back(v);
		go[++cnt] = v;nxt[cnt] = head[u];
		head[u] = cnt;
	}
	void clear() {
		for(int u:point) ins[u] = dfn[u] = low[u] = head[u] = 0;cnt = 0;
		point.clear();top = 0;
	}
	void tarjan(int u) {
		dfn[u] = low[u] = ++cnt;
		stk[++top] = u,ins[u] = 1;
		for(int e = head[u];e;e = nxt[e]) {
			int v = go[e];
			if(!dfn[v]) {
				tarjan(v);
				low[u] = min(low[u],low[v]);
			} else if(ins[v]) low[u] = min(low[u],dfn[v]);
		}
		if(dfn[u] == low[u]) {
			do{
				uni.merge(u,stk[top]);
				ins[stk[top]] = 0;
			}while(stk[top--] != u);
		}
	}
	void work() {cnt=0;for(int i:point) if(!dfn[i]) tarjan(i);}
}tar;

void solve(int l,int r,vector<int>d)
{
//	printf("l=%d,r=%d\\n",l,r);
	if(d.empty()) return ;
	if(l == r) {for(int u:d) E[u].tim = l;return ;}
	int mid = l+r>>1,cur = uni.top;
	for(int i:d) {
		if(E[i].tim <= mid) tar.add(uni.find(E[i].u),uni.find(E[i].v));
	}
	tar.work();	tar.clear();
	vector<int>dd1,dd2;
	for(int i:d) {
		int u = uni.find(E[i].u),v = uni.find(E[i].v);
		if(u == v && E[i].tim <= mid) dd1.push_back(i);
		else dd2.push_back(i);
	}
	solve(mid+1,r,dd2);uni.del(cur);solve(l,mid,dd1);
}

struct SBT{
	int tre[N<<3],ls[N<<3],rs[N<<3],tot,rt[N],siz[N<<3];
	void modify(int &root,int l,int r,int p,int x)
	{
		if(!root) root = ++tot;int mid = l+r>>1;
		if(l == r) {tre[root] += x*p,siz[root] += x;return ;}
		if(p <= mid) modify(ls[root],l,mid,p,x);
		else modify(rs[root],mid+1,r,p,x);
		tre[root] = tre[ls[root]] + tre[rs[root]];
		siz[root] = siz[ls[root]] + siz[rs[root]];
	}
	int merge(int a,int b)
	{
		if(!a || !b) return a+b;
		tre[a] += tre[b];siz[a] += siz[b];
		ls[a] = merge(ls[a],ls[b]),rs[a] = merge(rs[a],rs[b]);
		return a;
	}
	int query(int root,int l,int r,int x)
	{
		if(l == r) return min(x,siz[root])*l;int mid = l+r>>1;
		if(x == siz[rs[root]]) return tre[rs[root]];
		if(x < siz[rs[root]]) return query(rs[root],mid+1,r,x);
		if(x > siz[rs[root]]) return tre[rs[root]] + query(ls[root],l,mid,x-siz[rs[root]]);
	}

	void Merge(int a,int b) {
		rt[uni.find(a)] = merge(rt[a],rt[b]);
	}
	void modify(int root,int x,int op)	{
		modify(rt[root],1,(int)(1e9),x,op);
	}
}ds;
bool cmp(quer a,quer b) {return a.tim!=b.tim?a.tim < b.tim:a.op<b.op;}
signed main()
{
	// freopen("random.in","r",stdin);
	// freopen("sol.out","w",stdout);
	n = read(),m = read(),q = read();uni.init(n);
	for(int i = 1;i <= n;i ++) s[i] = read();
	for(int i = 1;i <= m;i ++) {
		E[i].u = read(),E[i].v = read();
		mp[make_pair(E[i].u,E[i].v)] = i;
	}
	for(int i = 1;i <= q;i ++) {
		Q[i].op = read(),Q[i].a = read(),Q[i].b = read(),Q[i].tim = q-i+1;
		if(Q[i].op == 1) {
			E[mp[make_pair(Q[i].a,Q[i].b)]].tim = q-i+1;
		} else if(Q[i].op == 2) s[Q[i].a] += Q[i].b;
	}
	vector<int>dd;
	for(int i = 1;i <= m;i ++) dd.push_back(i);
	solve(0,q+1,dd);int tot = m;//return 0;
	for(int i = 1;i <= m;i ++) {
		cal[i].op = 1,cal[i].tim = E[i].tim,cal[i].a = E[i].u,cal[i].b = E[i].v;
	}
	for(int i = 1;i <= q;i ++) {
		if(Q[i].op == 1) continue;
		cal[++tot] = Q[i];
	}
	for(int i = 1;i <= n;i ++) ds.modify(i,s[i],1);
	sort(cal+1,cal+1+tot,cmp);uni.init(n);
	for(int i = 1;i <= tot;i ++) {
		if(cal[i].op == 1) {
			int a = uni.find(cal[i].a),b = uni.find(cal[i].b);
			uni.merge(a,b);if(a != b) ds.Merge(a,b);
		} else if(cal[i].op == 2) {
			int a = uni.find(cal[i].a),b = cal[i].a;
			ds.modify(a,s[b],-1);
			s[b] -= cal[i].b;
			ds.modify(a,s[b],1);
		} else {
			int a = uni.find(cal[i].a),b = cal[i].b;
			ans[i] = ds.query(ds.rt[a],1,(int)(1e9),b);
		}
	}
	for(int i = tot;i >= 1;i --) if(ans[i]) printf("%lld\\n",ans[i]);
	return 0;
}

以上是关于WD与地图题解的主要内容,如果未能解决你的问题,请参考以下文章

luoguP5161 WD与数列 后缀自动机+线段树合并+启发式合并

谷歌地图在Android的标签片段上显示为灰色

谷歌地图不显示在片段中

如何在用java创建的布局内创建地图片段(GoogleMap)?

luogu P5162 WD与积木 FFT

谷歌地图片段内的片段可以操纵我的地图