您的位置:首页 > 其它

【BZOJ 2733】【HNOI 2012】永无乡【treap启发式合并】

2017-03-17 21:22 417 查看

Description

永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。

Input

输入文件第一行是用空格隔开的两个正整数 n 和 m,分别 表示岛的个数以及一开始存在的桥数。接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi,表示一开始就存 在一座连接岛 ai 和岛 bi 的桥。后面剩下的部分描述操作,该部分的第一行是一个正整数 q, 表示一共有 q 个操作,接下来的 q 行依次描述每个操作,操作的格式如上所述,以大写字母 Q 或B 开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。

对于 20%的数据 n≤1000,q≤1000

对于 100%的数据 n≤100000,m≤n,q≤300000

题解

看到对第k大值得查询,就能想到treap,这道题是用treap的启发式合并。

先用并查集维护集合,在合并treap时,将规模小的之间的元素一个一个插入规模大的里面。(至于复杂度什么的我也不会证,好像是O(n (log n)^2)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;

#define N 100010

int f
,size
;
int n,m,u,v,q;

struct node{
node *ch[2];
int r,v,s;
node(int x) { r = rand();v = x;ch[0] = ch[1] = NULL;s = 1;}
int cmp(int x)
{
if(x == v) return -1;
else return x > v ? 1 : 0;
}
void maintain()
{
s = 1;
if(ch[0]) s += ch[0]->s;
if(ch[1]) s += ch[1]->s;
}
}*root
;

int find(int x){return x == f[x] ? x : f[x] = find(f[x]);}

void rotate(node* &o,int d)
{
node* k = o->ch[d^1];
o->ch[d^1] = k->ch[d];
k->ch[d] = o;
o->maintain();k->maintain();
o = k;
}

void insert(node* &o,int x)
{
if(o == NULL) o = new node(x);
else
{
int d = o->cmp(x);
insert(o->ch[d],x);
if(o->ch[d]->r > o->r) rotate(o,d^1);
}
o->maintain();
}

void del(node* &o,int fa)
{
if(o->ch[1]) del(o->ch[1],fa);
if(o->ch[0]) del(o->ch[0],fa);
insert(root[fa],o->v);
delete(o);
}

void combine(int x,int y)
{
int fa = find(x),fb = find(y);
if(size[fa] < size[fb]) swap(fa,fb);
if(fa != fb)
{
f[fb] = fa;
size[fa] += size[fb];
del(root[fb],fa);
}
}

int kth(node* o,int k)
{
if(o == NULL || k <= 0 || k > o->s) return -1;
int s = (o->ch[0]==NULL ? 0 : o->ch[0]->s);
if(k == s + 1) return o->v;
else if(k <= s) return kth(o->ch[0],k);
else return kth(o->ch[1],k-s-1);
}

int k
;
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++)
{
scanf("%d",&u);
k[u] = i;
insert(root[i],u);
f[i] = i;
size[i] = 1;
}
for(int i = 1;i <= m;i++)
{
scanf("%d%d",&u,&v);
combine(u,v);
}
scanf("%d",&q);
char str[3];
while(q--)
{
scanf("%s%d%d",str,&u,&v);
if(str[0] == 'Q')
{
int fa = find(u);
if(v > size[fa]) puts("-1\n");
else printf("%d\n",k[kth(root[fa],v)]);
}
if(str[0] == 'B') combine(u,v);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  treap