P3224 [HNOI2012]永无乡

$ \color{#0066ff}{ 题目描述 }$

永无乡包含 \(n\) 座岛,编号从 \(1\)\(n\) ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 \(n\) 座岛排名,名次用 \(1\)\(n\) 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛 \(a\) 出发经过若干座(含 \(0\) 座)桥可以 到达岛 \(b\) ,则称岛 \(a\) 和岛 \(b\) 是连通的。

现在有两种操作:

B x y 表示在岛 \(x\) 与岛 \(y\) 之间修建一座新桥。

Q x k 表示询问当前与岛 \(x\) 连通的所有岛中第 \(k\) 重要的是哪座岛,即所有与岛 \(x\) 连通的岛中重要度排名第 \(k\) 小的岛是哪座,请你输出那个岛的编号。

\(\color{#0066ff}{输入格式}\)

第一行是用空格隔开的两个正整数 \(n\)\(m\) ,分别表示岛的个数以及一开始存在的桥数。

接下来的一行是用空格隔开的 \(n\) 个数,依次描述从岛 \(1\) 到岛 \(n\) 的重要度排名。随后的 \(m\) 行每行是用空格隔开的两个正整数 \(a_i\)\(b_i\) ,表示一开始就存在一座连接岛 \(a_i\) 和岛 \(b_i\) 的桥。

后面剩下的部分描述操作,该部分的第一行是一个正整数 \(q\) ,表示一共有 \(q\) 个操作,接下来的 \(q\) 行依次描述每个操作,操作的 格式如上所述,以大写字母 \(Q\)\(B\) 开始,后面跟两个不超过 \(n\) 的正整数,字母与数字以及两个数字之间用空格隔开。

\(\color{#0066ff}{输出格式}\)

对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表示所询问岛屿的编号。如果该岛屿不存在,则输出\(-1\)

\(\color{#0066ff}{输入样例}\)

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

\(\color{#0066ff}{输出样例}\)

-1
2
5
1
2

\(\color{#0066ff}{数据范围与提示}\)

对于 20% 的数据 \(n \leq 1000, q \leq 1000\)

对于 100% 的数据 \(n \leq 100000, m \leq n, q \leq 300000\)

\(\color{#0066ff}{题解}\)

B操作,平衡树暴力合并,因为最多\(logn\)次,所以没毛病

Q操作,Splay的kth就行

注意插入的时候要保证为根,先splay上去!

然而不splay上去也能A,数据水

#include<bits/stdc++.h>
#define LL long long
LL in() {
	char ch; LL x = 0, f = 1;
	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
	return x * f;
}
const int maxn = 3e5 + 10;
int n, m;
struct Splay {
protected:
	struct node {
		node *ch[2], *fa;
		int val, siz;
		node(int val = 0, int siz = 0): val(val), siz(siz) { fa = ch[0] = ch[1] = NULL; }
		void upd() { siz = (ch[0]? ch[0]->siz : 0) + (ch[1]? ch[1]->siz : 0) + 1; }
		int rk() { return ch[0]? ch[0]->siz + 1 : 1; }
		bool isr() { return this == fa->ch[1]; }
		void clr() {
			siz = 1;
			if(ch[0]) ch[0]->fa = NULL;
			if(ch[1]) ch[1]->fa = NULL;
			if(fa) fa->ch[val > fa->val] = NULL;
			fa = ch[0] = ch[1] = NULL;
		}
	}pool[maxn];
	void rot(node *x) {
		node *y = x->fa, *z = y->fa;
		bool k = x->isr(); node *w = x->ch[!k];
		if(z) z->ch[y->isr()] = x;
		(x->ch[!k] = y)->ch[k] = w;
		(y->fa = x)->fa = z;
		if(w) w->fa = y;
		y->upd(), x->upd();
	}
	void splay(node *o) {
		while(o->fa) {
			if(o->fa->fa) rot(o->isr() ^ o->fa->isr()? o : o->fa);
			rot(o);
		}
	}
	int kth(node *o, int k) {
		splay(o);
		if(o->siz < k) return -1;
		while(o && o->rk() != k) {
			if(o->rk() > k) o = o->ch[0];
			else k -= o->rk(), o = o->ch[1];
		}
		return o - pool;
	}
	void ins(node *rt, node *o) {
		splay(rt);
		o->clr();
		node *now = rt, *fa = NULL;
		while(now) fa = now, now = now->ch[o->val > now->val];
		o->fa = fa;
		fa->ch[o->val > fa->val] = o;
		splay(o);
	}

	void dfs(node *o, node *rt) {
		if(o->ch[0]) dfs(o->ch[0], rt);
		if(o->ch[1]) dfs(o->ch[1], rt);
		ins(rt, o);
	}
	node *findroot(node *o) { while(o->fa) o = o->fa; return o; }
	void outtree(node *o) {
		if(o->ch[0]) outtree(o->ch[0]);
		if(o->ch[1]) outtree(o->ch[1]);
	}
public:
	void init(int *val, int n) { for(int i = 1; i <= n; i++) pool[i] = node(val[i], 1); }
	void link(int x, int y) {
		node *a = pool + x, *b = pool + y;
		if(findroot(a) == findroot(b)) return;
		splay(a), splay(b);
		dfs(a, b);
	}
	int query(int x, int k) { return kth(pool + x, k); }
}s;
int val[maxn];
char getch() {
	char ch;
	while(!isalpha(ch = getchar()));
	return ch;
}

int main() {
	n = in(), m = in();
	for(int i = 1; i <= n; i++) val[i] = in();
	s.init(val, n);
	int x, y;
	for(int i = 1; i <= m; i++) x = in(), y = in(), s.link(x, y);
	for(int T = in(); T --> 0;) {
		if(getch() == 'Q') {
			x = in(), y = in();
			printf("%d\n", s.query(x, y));
		} else {
			x = in(), y = in();
			s.link(x, y);
		}
	}
	return 0;
}
posted @ 2019-02-26 07:21  olinr  阅读(162)  评论(0编辑  收藏  举报