【整体二分+线段树合并+tarjan】LGP5163 WD与地图

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/85623390

【题目】
原题地址
给定一幅有向图,支持三种操作:

  • 删去一条边
  • 给一个点权值增加 x x
  • x x 点所在强连通分量权值前 k k 大的和。
    n , m , q 1 0 5 n,m,q\leq 10^5

【解题思路】
我好像不是很会做啊(其实是自己s*了)。

首先倒序操作,那么删边可以变成加边。一个 naive \text{naive} 的做法是我们对每个点维护一棵权值线段树,每次增加一条边 ( x , y ) (x,y) ,我们就从 y y x x 跑缩边,将全部权值线段树合并一下。询问就在权值线段树上二分即可。
这样做的复杂度是 O ( m q + n log n + q log n ) O(mq+n\log n+q\log n) 的。

实际上瓶颈在于无法快速知道哪条边在什么时候被缩起来,否则我们可以按时间进行线段树合并。

考虑到边被缩起来的时间满足可二分性,于是我们用整体二分的思想:将出现时间 m i d \leq mid 的边全部加入图中跑缩边,我们可以知道在 m i d \leq mid 时间内有哪些边被缩掉。同时我们还需要用可持久化数组(雾)来维护一下是否在一个强连通分量内以及 d f n dfn (这样不用重复遍历边了)。每条边在每个分治深度下贡献一次,那么复杂度就是 O ( n log n α ( n ) ) O(n\log n\cdot \alpha (n)) 了。

这种知识点题的思考过程:欸我要一个tarjan,码码码;欸我要一个线段树合并,码码码;欸我要一个整体二分,码码码。然后就200+行了。

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
const int N=4e5+10,M=N*50;
int tn,n,m,Q,cnt,val[N];
vector<ll>ans;
set<pii>E;
ll res[N];

namespace IO
{
	int read()
	{
		int ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(ll x)
	{
		if(x>9) write(x/10);
		putchar(x%10^48);
	}
	void writeln(ll x){write(x);puts("");}
}
using namespace IO;

namespace Segment
{
	int sz,ls[M],rs[M],cnt[M],rt[N];
	ll sum[M];
	void update(int &x,int l,int r,int p,int v)
	{
		if(!x)x=++sz;cnt[x]+=v;sum[x]+=(ll)p*v;
		if(l==r) return;
		int mid=(l+r)>>1;
		if(p<=mid) update(ls[x],l,mid,p,v);
		else update(rs[x],mid+1,r,p,v);
	}
	ll query(int x,int l,int r,int p)
	{
		if(p>=cnt[x]) return sum[x];
		if(l==r) return sum[x]/cnt[x]*p;
		int mid=(l+r)>>1;
		if(cnt[rs[x]]>=p) return query(rs[x],mid+1,r,p);
		else return query(ls[x],l,mid,p-cnt[rs[x]])+sum[rs[x]];
	}
	int Tmerge(int x,int y)
	{
		if(!x || !y) return x+y;
		cnt[x]+=cnt[y];sum[x]+=sum[y];
		ls[x]=Tmerge(ls[x],ls[y]);
		rs[x]=Tmerge(rs[x],rs[y]);
		return x;
	}
}
using Segment::update;
using Segment::query;
using Segment::Tmerge;
using Segment::rt;

namespace DSU
{
	int fa[N];
	int findf(int x){return fa[x]==x?x:fa[x]=findf(fa[x]);}
	void merge(int x,int y)
	{
		x=findf(x);y=findf(y);
		if(x==y) return;
		if(x>y) swap(x,y);
		fa[x]=y;
	}
}
using namespace DSU;

namespace Graph
{
	int ind,tot;
	int low[N],dfn[N],ins[N],head[N];
	stack<int>st;
	struct edge
	{
		int u,v,t;
		edge(int _u=0,int _v=0,int _t=0):u(_u),v(_v),t(_t){}
	}g[N],cpy[N];
	vector<edge>mp[N];
	struct Tway
	{
		int v,nex;
		Tway(int _v=0,int _nex=0):v(_v),nex(_nex){}
	}e[N];
	void addedge(int u,int v){e[++tot]=Tway(v,head[u]);head[u]=tot;}
	void clear(int x){low[x]=dfn[x]=ins[x]=0;}
	void tarjan(int x)
	{
		dfn[x]=low[x]=++ind;ins[x]=1;st.push(x);
		for(int i=head[x];i;i=e[i].nex)
		{
			int v=e[i].v;
			if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
			else if(ins[v]) low[x]=min(low[x],dfn[v]);
		}
		if(low[x]^dfn[x]) return;
		for(int t=0;t^x;t=st.top(),st.pop()) merge(st.top(),x),ins[st.top()]=0;
	}
}
using Graph::addedge;
using Graph::tarjan;
using Graph::dfn;
using Graph::edge;
using Graph::mp;
using Graph::g;
using Graph::cpy;

namespace Divide_Conquer
{
	pii arr[N];
	void solve(int l,int r,int L,int R)
	{
		//cerr<<l<<" "<<r<<" "<<L<<" "<<R<<endl;
		if(l==r)
		{ 
			for(int i=L;i<=R;++i) mp[l].pb(g[i]);
			return;
		}
		if(L==R)
		{
			mp[(findf(g[L].u)==findf(g[L].v))?g[L].t:Q+1].pb(g[L]);
			return;
		}
		int mid=(l+r)>>1,c=0;
		for(int i=L;i<=R;++i) if(g[i].t<=mid)
		{
			int u=findf(g[i].u),v=findf(g[i].v);
			arr[++c]=mkp(u,v);Graph::head[u]=Graph::head[v]=0;
		}
		Graph::tot=0;
		for(int i=1;i<=c;++i)
		{
			int u=arr[i].fi,v=arr[i].se;addedge(u,v);
			Graph::clear(u);Graph::clear(v);
		}
		//cerr<<c<<endl;
		//if(r==12500) exit(0);
		Graph::ind=0;
		while(!Graph::st.empty()) Graph::st.pop();
		for(int i=1;i<=c;++i)
		{
			if(!dfn[arr[i].fi]) tarjan(arr[i].fi);
			if(!dfn[arr[i].se]) tarjan(arr[i].se);
		}
		int tl=0,tr=L,t=1;vector<pii> cp;
		for(int i=L;i<=R;++i)
		{
			int fg=0;
			if(g[i].t<=mid)
			{
				int u=arr[t].fi,v=arr[t].se;++t;
				if(findf(u)==findf(v)) fg=1;
				cp.pb(mkp(u,fa[u]));cp.pb(mkp(v,fa[v]));
			}
			if(fg) g[tr++]=g[i]; else cpy[++tl]=g[i];
		}
		for(int i=tr;i<=R;++i) g[i]=cpy[i-tr+1];
		for(int i=1;i<=c;++i) fa[arr[i].fi]=arr[i].fi,fa[arr[i].se]=arr[i].se;
		if(L<tr) solve(l,mid,L,tr-1);
		for(vector<pii>::iterator it=cp.begin();it!=cp.end();++it) fa[(*it).fi]=(*it).se;
		//exit(0);
		if(tr<=R) solve(mid+1,r,tr,R);
	}
}

struct Tquery
{
	int t,a,b;
	Tquery(int _t=0,int _a=0,int _b=0):t(_t),a(_a),b(_b){}	
}qs[N];

int main()
{
#ifndef ONLINE_JUDGE
	freopen("LGP5163.in","r",stdin);
	freopen("LGP5163.out","w",stdout);
#endif
	n=read();m=read();Q=read();
	for(int i=1;i<=n;++i) val[i]=read(),fa[i]=i;
	for(int i=1,u,v;i<=m;++i) u=read(),v=read(),E.insert(mkp(u,v));
	for(int i=Q;i;--i)
	{
		int t=read(),a=read(),b=read();
		qs[i]=Tquery(t,a,b);
		if(t==1) g[++cnt]=edge(a,b,i),E.erase(mkp(a,b));
		else if(t==2) val[a]+=b;
	}
	for(set<pii>::iterator it=E.begin();it!=E.end();++it) g[++cnt]=edge((*it).fi,(*it).se,0);
	Divide_Conquer::solve(0,Q+1,1,m);
	for(int i=1;i<=n;++i) tn=max(tn,val[fa[i]=i]);
	for(int i=1;i<=n;++i) update(rt[i],1,tn,val[i],1);
	qs[0].t=1;
	for(int i=0;i<=Q;++i)
	{
		if(qs[i].t==1)
		{
			for(vector<edge>::iterator it=mp[i].begin();it!=mp[i].end();++it)
			{
				int u=(*it).u,v=(*it).v;
				u=findf(u);v=findf(v);
				if(u==v) continue;
				fa[v]=u;rt[u]=Tmerge(rt[u],rt[v]);
			}
		}
		else if(qs[i].t==2)
		{
			int x=findf(qs[i].a),&v=val[qs[i].a];
			update(rt[x],1,tn,v,-1);update(rt[x],1,tn,v-=qs[i].b,1);
		}
		else ans.pb(query(rt[findf(qs[i].a)],1,tn,qs[i].b));
	}
	for(int i=(int)ans.size()-1;~i;--i) writeln(ans[i]);//printf("%lld\n",ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/85623390
WD