Loading

P5163 WD与地图(可撤销并查集+整体二分+tarjan+线段树合并)

洛谷题目传送门

题目描述

CX 让 WD 研究的地图可以看做是 n 个点,m 条边的有向图,由于政府正在尝试优化人民生活,他们会废弃一些无用的道路来把省下的钱用于经济建设。

城市都有各自的发达程度 si。为了方便管理,政府将整个地图划分为一些地区,两个点 u,v 在一个地区当且仅当u,v 可以互相到达。政府希望知道一些时刻某个地区的前 k 发达城市的发达程度总和,以此推断建设的情况。

也就是说,共有三个操作:
1 a b 表示政府废弃了从 a 连向 b 的边,保证这条边存在。
2 a b 表示政府把钱用于建设城市 a,使其发达程度增加 b。
3 a b 表示政府希望知道 a 城市所在地区发达程度前 b 大城市的发达程度之和。如果地区中的城市不足 b 个输出该地区所有城市的发达程度总和。

解题思路

这是本蒟蒻做过的码量最长的题,而且正解不容易想到,不过其实也不难懂

Step 1

先来考虑一下简化版本,也就是变成无向图,那么很容易想到讲倒着来将删边变成加边,然后用值域线段树维护每个连通块,最后线段树合并就可以了
不会的可以做一下P3224 [HNOI2012]永无乡

Step 2

但是现在变成了有向图,连通块变成了强连通分量,然后就不好做了,因为连上某一条边后,两个强连通分量不一定会合并,只有再加入若干条边后,才有可能形成一个新的强连通分量
于是看了题解之后,知道要用整体二分,二分的目标是求出每条边什么时候会使它所连的两个强连通分量合并,也就是每条边什么时候开始有效,于是我们整体二分(L,R,l,r),表示处理第L~R条边,答案时间在l ~ r之内,取中间值mid,将出现时间小于mid的边加入图中,用tarjan求强连通分量,可以用并查集维护每个点所在的强连通分量,接着枚举每一条边,如果他所连的两个点在同一个强连通分量内,也就是在mid之前,两个强连通分量合并了,那么这条边的有效时间就在左区间,否则加入右区间,这样就求出了每个边的有效时间,可以把这个时间看成真正加入这条边,因为之前无效,然后再用线段树合并

Step 3

但是每次求tarjan太费时间了,所以求出l~mid的强连通分量之后,可以发现在mid ~r这段时间这些边也是有效的,所以不需要删除当前的强连通分量,直接进入右区间就行了,但是在进入左区间之前,就需要先把强连通分量删掉,这里采用可撤销并查集,也就是使用按秩合并的并查集就行了
Tips:代码过长,要认真调试

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL N = 8e5+7;
const LL M = 4e5+7;
const LL V = 1e7+1;
struct edge
{
	LL y,next;
}e[2*M];
LL link[N],t=0;
LL n,m,q;
void add(LL x,LL y)
{
	t++;
	e[t].y=y;
	e[t].next=link[x];
	link[x]=t;
}
LL Getid(LL x,LL y)
{
	for(LL i=link[x];i;i=e[i].next)
	{
		if(e[i].y==y)
		{
			return i;
		}
	}
}
struct node
{
	LL x,y,t,opt;
}p[M],pl[M],pr[M],ques[M];
inline LL read()
{
	LL X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
LL siz[N],fa[N];
LL Find(LL x)
{
	if(x==fa[x]) return x;
	return Find(fa[x]);
}
LL top=0,st[N][2];
void merge(LL x,LL y)
{
	LL fx=Find(x);
	LL fy=Find(y);
	if(fx==fy) return;
	if(siz[fx]>siz[fy]) swap(fx,fy);
	fa[fx]=fy;
	st[++top][0]=fx;
	st[top][1]=fy;
	siz[fy]+=siz[fx];
}
LL val[N],dct[N],cntv=0;
LL sp[N];
LL dfn[N],low[N],num;
bool vis[N];
stack<LL> scc;
void tarjan(LL x)
{
	dfn[x]=low[x]=++num;
	vis[x]=1;
	scc.push(x);
	for(LL i=link[x];i;i=e[i].next)
	{
		LL y=e[i].y;
		if(!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(vis[y]) low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x])
	{
		LL rot=scc.top(),y;
		scc.pop();
		vis[rot]=0;
		if(rot==x) return;
		do
		{
			y=scc.top();
			scc.pop();
			vis[y]=0;
			merge(rot,y);
		}while(x!=y);
	}
}
void clear(LL tot)
{
	t=0;
	num=0;
	for(LL i=1;i<=tot;i++)
	{
		LL x=sp[i];
		link[x]=0;
		dfn[x]=0;
		low[x]=0;
	}
}
void back(LL base)
{
	while(top>base)
	{
		LL x=st[top][0],y=st[top][1];
		fa[x]=x;
		siz[y]-=siz[x];
		top--;
	}
}
void solve(LL L,LL R,LL l,LL r)
{
	if(L>R) return;
	if(l==r)
	{
		for(LL i=L;i<=R;i++)
		p[i].t=l;
		return;
	}
	LL mid=(l+r)>>1;
	LL lastop=top,tot=0,topl=0,topr=0;
	for(LL i=L;i<=R;i++)
	{
		if(p[i].t>mid) continue;
		LL x=Find(p[i].x);
		LL y=Find(p[i].y);
		add(x,y);
		sp[++tot]=x;
		sp[++tot]=y;
	}
	for(LL i=1;i<=tot;i++)
	if(!dfn[sp[i]]) tarjan(sp[i]);
	for(LL i=L;i<=R;i++)
	{
		LL x=p[i].x,y=p[i].y;
		if(p[i].t<=mid&&Find(x)==Find(y)) pl[++topl]=p[i];
		else pr[++topr]=p[i];
	}
	for(LL i=1;i<=topl;i++)
	p[L+i-1]=pl[i];
	for(LL i=1;i<=topr;i++)
	p[L+topl+i-1]=pr[i];
	clear(tot);
	solve(L+topl,R,mid+1,r);
	back(lastop);
	solve(L,L+topl-1,l,mid);
}
LL ask(LL x)
{
	return lower_bound(dct+1,dct+cntv+1,x)-dct;
}
LL lson[V],rson[V],rot[N];
LL sum[V],ans[N],cnt[V],All=0;
void pushup(LL k)
{
	cnt[k]=cnt[lson[k]]+cnt[rson[k]];
	sum[k]=sum[lson[k]]+sum[rson[k]];
}
void update(LL &k,LL l,LL r,LL x,LL v)
{
	if(!k) k=++All;		
	cnt[k]+=v;
	sum[k]+=dct[x]*v;
	if(l==r) return;
	LL mid=(l+r)>>1;
	if(x<=mid) update(lson[k],l,mid,x,v);
	else update(rson[k],mid+1,r,x,v);
}
LL query(LL k,LL l,LL r,LL K)
{
	if(K>cnt[k]) return sum[k];
	if(l==r) return dct[l]*K;
	LL mid=(l+r)>>1;
	if(K<=cnt[rson[k]]) return query(rson[k],mid+1,r,K);
	else return query(lson[k],l,mid,K-cnt[rson[k]])+sum[rson[k]];
}
LL Merge(LL x,LL y,LL l,LL r)
{
	if(!x||!y) return x+y;
	if(l==r)
	{
		cnt[x]+=cnt[y];
		sum[x]+=sum[y];
		return x;
	}
	LL mid=(l+r)>>1;
	lson[x]=Merge(lson[x],lson[y],l,mid);
	rson[x]=Merge(rson[x],rson[y],mid+1,r);
	pushup(x);
	return x;
}
bool cmp(node x,node y)
{
	if(x.t==y.t) return x.opt<y.opt;
	return x.t<y.t;
}
int main()
{
	n=read();
	m=read();
	q=read();
	for(LL i=1;i<=n;i++)
	{
		val[i]=read();
		dct[++cntv]=val[i];
		fa[i]=i;
		siz[i]=1;
	}
	for(LL i=1;i<=m;i++)
	{
		LL x=read();
		LL y=read();
		add(x,y);
		p[i].x=x;
		p[i].y=y;
	}
	for(LL i=1;i<=q;i++)
	{
		LL opt,x,y;
		opt=read();
		x=read();
		y=read();
		ques[i].opt=opt;
		ques[i].x=x;
		ques[i].y=y;
		ques[i].t=(q-i+1);
		if(opt==1) p[Getid(x,y)].t=ques[i].t;
		if(opt==2)
		{
			val[x]+=y;
			dct[++cntv]=val[x];
		}
	}
	memset(link,0,sizeof(link));
	t=0;
	solve(1,m,0,q+1);
	sort(dct+1,dct+cntv+1);
	cntv=unique(dct+1,dct+cntv+1)-dct-1;
	for(LL i=1;i<=n;i++)
	{		
		update(rot[i],1,cntv,ask(val[i]),1);
		fa[i]=i;
		siz[i]=1;
	}
	LL tott=m;
	for(LL i=1;i<=q;i++)
	{
		if(ques[i].opt!=1)
		{
			p[++tott]=ques[i];
		}
	}
	sort(p+1,p+tott+1,cmp);
	
	for(LL i=1;i<=tott;i++)
	{
		LL x=p[i].x;
		LL y=p[i].y;
		LL t=p[i].t;
		LL opt=p[i].opt;
		if(!opt)
		{
			x=Find(x);
			y=Find(y);
			if(x==y) continue;
			if(siz[x]<siz[y]) swap(x,y);
			fa[y]=x;
			siz[x]+=siz[y];
			rot[x]=Merge(rot[x],rot[y],1,cntv); 
		}
		else if(opt==2)
		{
			LL Rot=Find(x);
			update(rot[Rot],1,cntv,ask(val[x]),-1);
			val[x]-=y;
			update(rot[Rot],1,cntv,ask(val[x]),1);
		}
		else
		{
			LL Rot=Find(x);
			ans[q-t+1]=query(rot[Rot],1,cntv,y);
		}
	}
	for(LL i=1;i<=q;i++)
	if(ques[i].opt==3) printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2021-08-11 20:10  Larunatrecy  阅读(51)  评论(0)    收藏  举报