P5163 WD与地图

发布时间 2023-12-27 11:33:13作者: WrongAnswer_90

更好的阅读体验

P5163 WD与地图

喵喵题,但其实没有那么难。

删边倒序转成加边是显然的,询问可以通过值域线段树合并实现,修改,合并,查询都是好做的。考虑如何维护动态加边的 SCC。

难点是每个时刻缩点后的图是一个 DAG,并不像无向图的搜索树一样好维护,而且新加入的边可能不会立刻构成 SCC 而是和再之后加入的边构成。

简化问题,可以通过计算出每条边被并到某个 SCC 里的时刻来维护。发现每条边只会被并到某个 SCC 里一次(之后的合并是边所在的 SCC 和其他的 SCC 合并,和这条边无关),满足单调性。并且需要对于每条边都求出这个时间,考虑整体二分。

简单的想法是,对于当前的二分区间,加入 \(l\sim mid\) 间的新边和这个区间询问(询问是指对一条边什么时候合并的询问)的所有边,跑一遍 Tarjan 求出 SCC,对于所有询问边,已经属于一个 SCC 的全部向左侧递归。对于在右侧的,先加入左侧的所有边,然后向右侧递归。

但是上面的复杂度是假的,原因很显然,每次 Tarjan 的复杂度和 \(r\) 有关而不是和 \(r-l+1\) 有关,其本质是没有利用好 SCC 的性质,加入了大量的无用边导致复杂度退化。

把上面的做法稍作改进:跑完 Tarjan 之后,把 SCC 内所有点用并查集合并在一起,然后向右侧递归。加边的时候加入 \((find(x)-find(y))\) 这样每次跑 Tarjan 的时候复杂度就挂在了边数上。然后向左侧递归的时候需要把并查集取消,可以用可撤销并查集实现,总复杂度 \(\mathcal O(n\log^2n)\)。(另一种实现方式是把右侧边进行重标号,可以消去并查集的一只 \(\log\),本人采用并查集写法)

注意实现细节:询问边有一部分就是新边,二分的在某个区间一个已经被删掉的询问边可能还没被加进来;注意特判开始就在 SCC 里的边和最后也没被合并的边。细节挺多的,写了一上午,但是一遍过?离谱。

也算是比较重工业的题了,代码写得很丑,见谅/wul。

	int n,m,q,len,all,vis[200010],ans[200010],a[200010],numa[400010];
	pii bb[200010];
	tup b[200010],c[200010],le[200010],ri[200010],d[200010];
	map<pii,int> hash;
	vector<pii> edge;
	vector<int> ver,del[200010],ve[200010];
	#define id(x,y) (x-1)*100000+y
	namespace RDSU
	{
		int fa[200010],siz[200010];
		stack<int> st;
		inline void init(){for(int i=1;i<=n;++i)siz[i]=1,fa[i]=i;}
		inline int find(int x){while(x!=fa[x])x=fa[x];return x;}
		inline bool Dmerge(int x,int y)
		{
			if((x=find(x))==(y=find(y)))return 0;
			if(siz[x]>siz[y])st.e(y),siz[x]+=siz[y],fa[y]=x;
			else st.e(x),siz[y]+=siz[x],fa[x]=y;
			return 1;
		}
		inline void cancel(){int x=st.top();st.pop();siz[fa[x]]-=siz[x],fa[x]=x;}
	}
	using namespace RDSU;
	namespace Segment
	{
		int root[200010],cnt;
		struct{int ls,rs,val;ll sum;}t[16000010];
		inline void update(int now)
		{
			t[now].val=t[t[now].ls].val+t[t[now].rs].val;
			t[now].sum=t[t[now].ls].sum+t[t[now].rs].sum;
		}
		void change(int&now,int x,int y,int L=0,int R=len)
		{
			if(!now)now=++cnt;
			t[now].val+=numa[x]*y,t[now].sum+=y;
			if(L==R)return void();
			int mid=L+((R-L)>>1);
			if(x<=mid)change(t[now].ls,x,y,L,mid);
			else change(t[now].rs,x,y,mid+1,R);
		}
		int merge(int x,int y,int L=0,int R=len)
		{
			if(!x||!y)return x|y;
			if(L==R)return t[x].val+=t[y].val,t[x].sum+=t[y].sum,x;
			int mid=L+((R-L)>>1);
			t[x].ls=merge(t[x].ls,t[y].ls,L,mid);
			t[x].rs=merge(t[x].rs,t[y].rs,mid+1,R);
			return update(x),x;
		}
		int ask(int now,int x,int L=0,int R=len)
		{
			if(x>=t[now].sum)return t[now].val;
			if(!x)return 0;
			if(L==R)return numa[L]*x;
			int mid=L+((R-L)>>1);
			if(x<=t[t[now].rs].sum)return ask(t[now].rs,x,mid+1,R);
			return ask(t[now].rs,x,mid+1,R)+ask(t[now].ls,x-t[t[now].rs].sum,L,mid);
		}
		void print(int p,int L=0,int R=len)
		{
			if(!p)return;
			if(L==R)return write('(',L,',',t[p].sum,')');
			int mid=L+((R-L)>>1);
			print(t[p].ls,L,mid),print(t[p].rs,mid+1,R);
		}
	}
	using namespace Segment;
	namespace Connection
	{
		int dfn[200010],low[200010],tot,num,col[200010],ins[200010];
		stack<int> st;
		void tarjan(int x)
		{
			st.e(x),dfn[x]=low[x]=++tot,ins[x]=1;
			for(auto to:ve[x])
			{
				if(!dfn[to])tarjan(to),low[x]=min(low[x],low[to]);
				else if(ins[to])low[x]=min(low[x],dfn[to]);
			}
			if(dfn[x]==low[x])
			{
				int y;++num;
				do ins[y=st.top()]=0,col[y]=num,st.pop();while(y!=x);
			}
		}
		
	}
	using namespace Connection;
	bitset<200010> viss;
	void solve(int l,int r,int L,int R)
	{
		if(L==R)
		{
			for(int i=l;i<=r;++i)
			if(vis[b[i].z]!=inf)
			del[L].eb(b[i].z);
			return;
		}
		if(l>r)return;
		int mid=L+((R-L)>>1),len1=0,len2=0;
		for(int i=L,x,y;i<=mid;++i)
		{
			if(vis[d[i].z]==inf)continue;
			x=find(d[i].x),y=find(d[i].y);
			if(!viss[x])viss[x]=1,ver.eb(x);
			if(!viss[y])viss[y]=1,ver.eb(y);
			ve[x].eb(y);
		}
		for(int i=l;i<=r;++i)
		{
			if(vis[b[i].z]>mid)continue;
			bb[i].fi=find(b[i].x),bb[i].se=find(b[i].y);
			if(!viss[bb[i].fi])viss[bb[i].fi]=1,ver.eb(bb[i].fi);
			if(!viss[bb[i].se])viss[bb[i].se]=1,ver.eb(bb[i].se);
			ve[bb[i].fi].eb(bb[i].se);
		}
		for(auto j:ver)if(!dfn[j])tarjan(j);
		vector<pii> ved;
		for(int i=l;i<=r;++i)
		{
			if(vis[b[i].z]<=mid&&col[bb[i].fi]==col[bb[i].se])
			ved.eb(bb[i].fi,bb[i].se),le[++len1]=b[i];
			else ri[++len2]=b[i];
		}
		for(auto j:ver)low[j]=dfn[j]=col[j]=0,ve[j].clear();
		ver.clear(),tot=num=0;
		for(int i=L;i<=mid;++i)viss[find(d[i].x)]=viss[find(d[i].y)]=0;
		for(int i=l;i<=r;++i)viss[bb[i].fi]=viss[bb[i].se]=0;
		for(int i=1;i<=len1;++i)b[l+i-1]=le[i];
		for(int i=1;i<=len2;++i)b[l+len1+i-1]=ri[i];
		solve(l,l+len1-1,L,mid);
		for(auto p:ved)Dmerge(p.fi,p.se);
		solve(l+len1,r,mid+1,R);
	}
	inline int vfind(int x){return lower_bound(numa+1,numa+1+len,x)-numa;}
	inline void Merge(int x,int y){if((x=find(x))!=(y=find(y)))root[x]=root[y]=merge(root[x],root[y]),Dmerge(x,y);}
	inline bool cmp(tup t1,tup t2){return t1.z<t2.z;}
	inline void mian()
	{
		read(n,m,q),init();
		for(int i=1;i<=n;++i)read(a[i]),numa[++len]=a[i];
		for(int i=1;i<=m;++i)read(b[i].x,b[i].y),hash[mp(b[i].x,b[i].y)]=b[i].z=i;
		for(int i=1;i<=q;++i)
		{
			read(c[i].x,c[i].y,c[i].z);
			if(c[i].x==1)d[++all]=b[hash[mp(c[i].y,c[i].z)]],vis[hash[mp(c[i].y,c[i].z)]]=all;
			if(c[i].x==2)numa[++len]=(a[c[i].y]+=c[i].z);
		}
		for(int i=1;i<=m;++i)if(vis[i])vis[i]=all-vis[i]+1;
		for(int i=1;i<=m;++i)ve[b[i].x].eb(b[i].y);
		for(int i=1;i<=n;++i)if(!dfn[i])tarjan(i);
		for(int i=1;i<=m;++i)if(col[b[i].x]!=col[b[i].y])vis[i]=inf;
		tot=num=0;
		for(int i=1;i<=n;++i)dfn[i]=low[i]=0,ve[i].clear(),col[i]=0;
		sort(numa+1,numa+1+len),len=unique(numa+1,numa+1+len)-numa-1;
		reverse(d+1,d+1+all),reverse(c+1,c+1+q),init();
		if(all)solve(1,m,1,all),sort(b+1,b+1+m,cmp);
		for(int i=1;i<=n;++i)change(root[i],vfind(a[i]),1);
		for(int i=1;i<=m;++i)if(!vis[i])ve[b[i].x].eb(b[i].y);
		for(int i=1;i<=n;++i)if(!dfn[i])tarjan(i);
		for(int i=1;i<=m;++i)if(!vis[i]&&col[b[i].x]==col[b[i].y])Merge(b[i].x,b[i].y);
		for(int i=1,t=0,x,y;i<=q;++i)
		{
			if(c[i].x==1)
			{
				++t;
				for(auto j:del[t])
				{
					if((x=find(b[j].x))!=(y=find(b[j].y)))
					Merge(x,y);
				}
			}
			else if(c[i].x==2)
			{
				change(root[find(c[i].y)],vfind(a[c[i].y]),-1);
				change(root[find(c[i].y)],vfind(a[c[i].y]-=c[i].z),1);
			}
			else ans[i]=ask(root[find(c[i].y)],c[i].z);
		}
		for(int i=q;i>=1;--i)if(c[i].x==3)write(ans[i],'\n');