一種並查集操作

基礎並查集鏈接


正文

現在有以下這個問題:
給定l,r,yl,r,y,對於x[l,r]x\in [l,r],我們需要將xxx+yx+y併到一起。
考慮類似RMQ的做法:
lognlogn個並查集,第i個並查集的xxyy聯通表示所有x+jx+jy+jy+j聯通(0i<2j0\leq i < 2^j)。也就是說,xxyy聯通,x+1x+1y+1y+1聯通,x+2x+2y+2y+2聯通……
這樣子就實現了原問題。
具體操作的時候,令操作爲 {l,y,z},表示需要對於任意的x[l,l+2z)x\in [l,l+2^z),將xxyy聯通
如果第z個並查集的xxyy已經聯通,則退出。
否則
如果z=0,直接聯通,否則遞歸執行第z-1個並查集。
每個並查集的每個層均攤O(n)O(n)次,複雜度O(nlogn)O(n log n)
事實上除了 xxx+yx+y併到一起 這個問題以外,其它對一段區間進行完全相同操作的詢問也可以類似處理。

代碼

#define fo(i,a,b) for(int i=a;i<=b;i++)
struct disjoint_set{
	int fa[N];
	int gf(int x){return fa[x]==x?x:fa[x]=gf(fa[fa[fa[x]]]);}
	void reset(){fo(i,1,n) fa[i]=i;}
	void link(int x,int y){fa[gf(x)]=gf(y);}
	bool linked(int x,int y){return gf(x)==gf(y);}
}b[20];

void work(int x,int y,int z)
{
	if(b[z].linked(x,x+y)) return;
	b[z].link(x,x+y);
	if(z==0) ans=ans+W,ans1++;
	else work(x,y,z-1),work(x+1<<(z-1),y,z-1);
}

例題【GDOI2019Day2模擬2019.4.29】Endless

Description

在這裏插入圖片描述

Input

在這裏插入圖片描述

Output

在這裏插入圖片描述

Sample Input

1
8
2 2 5 6 2 5 6 2
5 1 4 4

Sample Output

21

Solution

按照kruskal的做法,對不同長度的邊權排序。
設目前處理長度爲L的連邊。
將序列每L個位置放一個分界點。
相鄰兩個分界點求最長公共前綴和最長公共後綴。
就可以求出合法的長度爲2L 的區間
然後就是上面那個並查集問題了。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 1001000
#define ull unsigned long long
#define mo 1000000009ll
#define ll long long
using namespace std;
int a[N],n,lg[N],_[N],ans1=0;
ull s[N],e[N];
ll ans,W;
struct disjoint_set{
	int fa[N];
	int gf(int x){return fa[x]==x?x:fa[x]=gf(fa[fa[fa[x]]]);}
	void reset(){fo(i,1,n) fa[i]=i;}
	void link(int x,int y){fa[gf(x)]=gf(y);}
	bool linked(int x,int y){return gf(x)==gf(y);}
}b[20];
pair<ll,int>w[N];
ull get(int x,int y)
{
	if(x>y) swap(x,y);
	return (s[y]-s[x-1]*e[y-x+1]%mo+mo)%mo;
}
int ef(int x,int y,int r,int z)
{
	r++;
	if(a[x]!=a[y]) return 0;
	int l=1;
	while(l+1<r)
	{
		int m=(l+r)>>1;
		if(get(x,x+(m-1)*z)==get(y,y+(m-1)*z)) l=m;else r=m;
	}
	return l;
}
void work(int x,int y,int z)
{
	if(b[z].linked(x,x+y)) return;
	b[z].link(x,x+y);
	if(z==0) ans=ans+W,ans1++;
	else work(x,y,z-1),work(x+_[z-1],y,z-1);
}
int main()
{
	freopen("endless.in","r",stdin);
//	freopen("endless.out","w",stdout);
	_[0]=1;
	fo(i,1,19)
	{
		_[i]=_[i-1]*2;
		fo(j,_[i-1],_[i]-1) lg[j]=i-1;
	}
	int ac;scanf("%d",&ac);
	while(ac--)
	{
		scanf("%d",&n);
		fo(i,0,18) b[i].reset();
		e[0]=1,ans=0;
		fo(i,1,n) scanf("%d",&a[i]),s[i]=(s[i-1]*(ull)(19260819)+(ull)a[i])%mo,e[i]=(e[i-1]*(ull)(19260819))%mo;
		fo(i,1,n/2) scanf("%lld",&w[i].first),w[i].second=i;
		sort(w+1,w+n/2+1);
		fo(q,1,n/2)
		{
			int L=w[q].second;
			for(int i=1;i+L<=n;i+=L)
			{
				int j=i+L;
				int r=ef(i,j,n-j+1,1),l=ef(i,j,i,-1);
				if(l+r<=L) continue;
				l=i-l+1,r=i+r-1;
				int z=lg[r-l+1];
				W=w[q].first;
				work(l,L,z);
				work(r-_[z]+1,L,z);
			}
		}
		printf("%lld\n",ans);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章