1. 程式人生 > >永無鄉[Splay啟發式合併]

永無鄉[Splay啟發式合併]

題目描述

永無鄉包含 nn 座島,編號從 1 到 n ,每座島都有自己的獨一無二的重要度,按照重要度可以將這 n 座島排名,名次用 1 到 n 來表示。某些島之間由巨大的橋連線,通過橋可以從一個島到達另一個島。如果從島 a 出發經過若干座(含0 座)橋可以 到達島 b ,則稱島 a 和島 b 是連通的。

現在有兩種操作:

B x y 表示在島 x 與島 y 之間修建一座新橋。

Q x k 表示詢問當前與島 x 連通的所有島中第 k 重要的是哪座島,即所有與島 x 連通的島中重要度排名第 k 小的島是哪座,請你輸出那個島的編號。

輸入格式:

第一行是用空格隔開的兩個正整數 n 和 m ,分別表示島的個數以及一開始存在的橋數。

接下來的一行是用空格隔開的 n 個數,依次描述從島 1 到島 n 的重要度排名。隨後的 m 行每行是用空格隔開的兩個正整數 ai​ 和 bi​ ,表示一開始就存在一座連線島ai​ 和島bi​ 的橋。

後面剩下的部分描述操作,該部分的第一行是一個正整數 q,表示一共有 q 個操作,接下來的 q 行依次描述每個操作,操作的 格式如上所述,以大寫字母 Q 或 B 開始,後面跟兩個不超過 n 的正整數,字母與數字以及兩個數字之間用空格隔開。

輸出格式:

對於每個 Q x k 操作都要依次輸出一行,其中包含一個整數,表示所詢問島嶼的編號。如果該島嶼不存在,則輸出 -1 。

輸入樣例#1:

 

5  1
4  3 2 5 1
1  2
7
Q 3 2
Q 2 1
B 2 3
B 1 5
Q 2 1
Q 2 4
Q 2 3

輸出樣例#1: 

-1
2
5
1
2

對於 100% 的資料 n≤100000,m≤n,q≤300000

合併Splay+區間第k大

對於合併,我們把size小的一個點一個點拆開,然後插入到另一個裡面

聽起來很暴力,其實複雜度是正確的 

因為小的那個大小至少翻倍,所以一個元素最多查logn次

總的複雜度為O(nlongn^2)

#include<bits/stdc++.h>
#define N 100005
#define lc t[x].ch[0]
#define rc t[x].ch[1]
using namespace std;
struct Node{
    int ch[2],fa,size;
}t[N];
int n,m,q,rt[N],f[N],a[N],ans,tot;
char ch[20];
int read(){
    int cnt=0,f=1;char ch=0;
    while(!isdigit(ch)){ch=getchar();if(ch=='-')f=-1;}
    while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
    return cnt*f;
}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
void Pushup(int x){
	t[x].size=t[lc].size+t[rc].size+1;
}
void rotate(int x,int &goal){
	int y=t[x].fa,z=t[y].fa;
	int k=(t[y].ch[1]==x);
	if(y==goal) goal=x;
	t[z].ch[t[z].ch[1]==y]=x;
	t[x].fa=z;
	t[y].ch[k]=t[x].ch[k^1];
	t[t[x].ch[k^1]].fa=y;
	t[x].ch[k^1]=y;
	t[y].fa=x;
	Pushup(y),Pushup(x);
}
void splay(int x,int &goal){
	while(t[x].fa!=goal){
		int y=t[x].fa,z=t[y].fa;
		if(z!=goal)
			(t[y].ch[1]==x)^(t[z].ch[1]==y)?rotate(x,goal):rotate(y,goal);
		rotate(x,goal);
	}
}
void insert(int u,int &x,int fa){
    if(x==0){x=u;t[u].fa=fa;return;}
    t[x].size++;
    if(a[u]<=a[x]) insert(u,lc,x);
    else insert(u,rc,x);
}
void merge(int x,int y){
	if(x==y) return;
	if(t[rt[x]].size>t[rt[y]].size) swap(x,y);
	f[x]=y; 
	queue<int> q;
	q.push(rt[x]);
	while(!q.empty()){
		int u=q.front(); q.pop();
		if(t[u].ch[0]) q.push(t[u].ch[0]);
		if(t[u].ch[1]) q.push(t[u].ch[1]);
		insert(u,rt[y],0);
		splay(u,rt[y]);
	}
}
int Kth(int x,int k){
	if(t[lc].size>=k) return Kth(lc,k);
	else if(t[lc].size==k-1) return x;
	return Kth(rc,k-t[lc].size-1);
} 
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++) f[i]=i,rt[i]=i,t[i].size=1;
	for(int i=1;i<=m;i++){
		int x=read(),y=read();
		merge(find(x),find(y));
	}
	q=read();
	while(q--){
		scanf("%s",ch);
		int x=read(),y=read();
		if(ch[0]=='Q'){
			x=rt[find(x)];
			if(t[x].size<y) printf("-1\n");
			else printf("%d\n",Kth(x,y));
		}
		if(ch[0]=='B') merge(find(x),find(y));
	}
}