1. 程式人生 > 其它 >#分塊,可撤銷並查集#洛谷 5443 [APIO2019]橋樑

#分塊,可撤銷並查集#洛谷 5443 [APIO2019]橋樑

分塊,可撤銷並查集

題目


分析

最直接的做法就是線上一邊修改邊權,詢問直接全部重排,

然後用可撤銷並查集維護連通塊大小,這樣時間複雜度為 \(O(qm)\)

同樣儘量讓大部分的邊不需要修改邊權,那麼每 \(B\) 個操作整體進行一次,

那麼最多隻有 \(B\) 條邊會修改,剩下的邊直接用雙指標加入並查集。

\(B\) 條邊直接每次詢問修改邊權,然後排序加入並查集,詢問完與剩下的邊歸併排序

那麼時間複雜度為 \(O(qB\log n+\frac{m^2}{B}\log n)\),實際上 \(B\)\(\sqrt{m\log n}\) 跑得比較快。

注意可撤銷並查集不能通過siz的大小合併,只是為了方便,但可能會被卡。


程式碼

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=100011; struct node{int x,y,w;}e[N]; struct rec{int x,y,rk;}q[N],a[N];
int n,m,Q,bl,f[N],rk[N],siz[N],ans[N],Fa[N],tod,Fb[N],v[N],Rk[N],RK[N],rK[N];
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
bool cmp0(int x,int y){return e[x].w>e[y].w;}
bool cmp1(int x,int y){return q[x].y>q[y].y;}
int getf(int u){return f[u]==u?u:getf(f[u]);}
void Merge(int x,int y){
	int fa=getf(x),fb=getf(y);
	if (siz[fa]>siz[fb]) swap(fa,fb);
	if (fa==fb) return;
	f[fa]=fb,siz[fb]+=siz[fa],Fa[++tod]=fa,Fb[tod]=fb;
}
int main(){
	n=iut(),m=iut();
	for (int i=1;i<=m;++i) e[i]=(node){iut(),iut(),iut()};
	for (int i=1;i<=n;++i) f[i]=i,siz[i]=1;
	for (int i=1;i<=m;++i) rk[i]=i;
	Q=iut(),bl=sqrt(m*log(n)),sort(rk+1,rk+1+m,cmp0);
	for (int l=1,r;l<=Q;l+=bl){
		if (l+bl>Q) r=Q; else r=l+bl-1;
		int tot=0,cnt=0,TOT=0,ToT=0;
		for (int i=l;i<=r;++i){
			int opt=iut(),x=iut(),y=iut();
			if (opt==1) ++tot,a[tot]=(rec){x,y,tot},v[x]=l;
			    else q[++cnt]=(rec){x,y,tot},Rk[cnt]=cnt;
		}
		if (!cnt) continue;
		for (int i=1;i<=m;++i) if (v[i]==l) rK[++TOT]=i;
		sort(Rk+1,Rk+1+cnt,cmp1);
		for (int i=1,j=1;i<=cnt;++i){
			for (;j<=m&&e[rk[j]].w>=q[Rk[i]].y;++j)
			    if (v[rk[j]]!=l) Merge(e[rk[j]].x,e[rk[j]].y);
			for (int k=1;k<=q[Rk[i]].rk;++k) swap(e[a[k].x].w,a[k].y);
			int Tod=tod;
			for (int k=1;k<=TOT;++k)
			if (e[rK[k]].w>=q[Rk[i]].y)
			    Merge(e[rK[k]].x,e[rK[k]].y);
			ans[Rk[i]]=siz[getf(q[Rk[i]].x)];
			for (;tod>Tod;--tod) siz[Fb[tod]]-=siz[Fa[tod]],f[Fa[tod]]=Fa[tod];
		    for (int k=q[Rk[i]].rk;k;--k) swap(e[a[k].x].w,a[k].y);
		}
		for (;tod;--tod) siz[Fb[tod]]-=siz[Fa[tod]],f[Fa[tod]]=Fa[tod];
		for (int k=1;k<=tot;++k) swap(e[a[k].x].w,a[k].y);
		sort(rK+1,rK+1+TOT,cmp0);
		int I=1,J=1;
		while (I<=TOT){
			while (J<=m&&v[rk[J]]==l) ++J;
			if (J<=m&&e[rk[J]].w>=e[rK[I]].w) RK[++ToT]=rk[J++];
			    else RK[++ToT]=rK[I++];
		}
		for (;J<=m;++J) if (v[rk[J]]!=l) RK[++ToT]=rk[J];
		for (int i=1;i<=ToT;++i) rk[i]=RK[i];
		for (int i=1;i<=cnt;++i) print(ans[i]),putchar(10);
	}
	return 0;
}