您的位置:首页 > 其它

bzoj 2733 Splay 启发式合并,名次树

2015-02-05 19:51 302 查看
题意:给定一个带点权的无向图,有两种操作:

  1、将两个连通分量合并。

  2、查询某个连通分量里的第K大点。

题解:

用并查集维护连通关系,一开始建立n棵splay树,然后不断合并,查询。

处理技巧:

  1、每个顶点u所在的Splay就是T[find(u)]。

  2、每个顶点在树中对应的节点编号就是该顶点的编号。

#include <cstdio>
#include <iostream>
#define maxn 100110
using namespace std;

int key[maxn], pre[maxn], son[maxn][2], siz[maxn], ntot;
struct Splay {
int root;
Splay():root(0){}
void update( int nd ) {
siz[nd] = siz[son[nd][0]] + siz[son[nd][1]] + 1;
}
void rotate( int nd, int d ) {
int p = pre[nd];
int s = son[nd][!d];
int ss = son[s][d];

son[nd][!d] = ss;
son[s][d] = nd;
if( p ) son[p][ nd==son[p][1] ] = s;
else root = s;

pre[nd] = s;
pre[s] = p;
if( ss ) pre[ss] = nd;

update( nd );
update( s );
}
void splay( int nd, int top=0 ) {
while( pre[nd]!=top ) {
int p = pre[nd];
int nl = nd==son[p][0];
if( pre[p]==top ) {
rotate( p, nl );
} else {
int pp = pre[p];
int pl = p==son[pp][0];
if( nl==pl ) {
rotate( pp, pl );
rotate( p, nl );
} else {
rotate( p, nl );
rotate( pp, pl );
}
}
}
}
int initnode( int nd, int k, int p ) {
key[nd] = k;
pre[nd] = p;
son[nd][0] = son[nd][1] = 0;
siz[nd] = 1;
return nd;
}
void insert( int k, int nnd ) {
if( !root ) {
root = initnode(nnd,k,0);
return;
}
int nd = root;
while( son[nd][ k>key[nd] ] )
nd = son[nd][ k>key[nd] ];
son[nd][ k>key[nd] ] = initnode(nnd,k,nd);
update( nd );
splay( nd );
}
int nth( int n ) {
int nd = root;
while( 1 ) {
int ls = siz[son[nd][0]];
if( n<=ls ) {
nd = son[nd][0];
} else if( n>=ls+2 ) {
nd = son[nd][1];
n -= ls+1;
} else {
break;
}
}
splay(nd);
return nd;
}
inline int size() { return siz[root]; }
static void join( Splay &T , int snd ) {
if( !snd ) return;
join( T, son[snd][0] );
join( T, son[snd][1] );
T.insert( key[snd], snd );
}
};

int n, m, q;
int fa[maxn];
Splay T[maxn];

int find( int a ) {
return a==fa[a] ? a : fa[a]=find(fa[a]);
}

void join( int a, int b ) {
if( find(a)==find(b) ) return;
if( T[find(a)].size() > T[find(b)].size() ) swap(a,b);
Splay::join( T[find(b)], T[find(a)].root );
fa[find(a)] = find(b);
}

int main() {
scanf( "%d%d", &n, &m );
for( int i=1,w; i<=n; i++ ) {
scanf( "%d", &w );
fa[i] = i;
T[i].insert( w, i );
}
for( int i=1,u,v; i<=m; i++ ) {
scanf( "%d%d", &u, &v );
join(u,v);
}
scanf( "%d", &q );
while( q-- ) {
char ch[2];
int a, b;
scanf( "%s%d%d", ch, &a, &b  );
if( ch[0]=='B' ) {
join(a,b);
} else {
if( !(1<=b&&b<=T[find(a)].size()) ) printf( "-1\n" );
else printf( "%d\n", T[find(a)].nth(b) );
}
}
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: