题解 P3224 【[HNOI2012]永无乡】
zhengrunzhe · · 题解
指针splay来一发
思维各位dalao都讲的很清楚了
每一个岛都建立一个存储重要值的splay
合并直接把小树的每一个节点都insert到大树里就好了
查询就是查排名(注意输出-1…
#include<cstdio>
#include<algorithm>
const int N=100001;
inline int read()
{
int out=0;char ch=getchar();
while (ch<48||ch>57)ch=getchar();
while (ch>47&&ch<58)out=(out<<3)+(out<<1)+ch-48,ch=getchar();
return out;
}
namespace Splay //Splay
{
struct tree
{
int value,size;
tree *fa,*son[2];
inline tree(int key)
{
size=1;
value=key;
fa=son[0]=son[1]=NULL;
}
inline void pushup()
{
size=1;
if (son[0])size+=son[0]->size;
if (son[1])size+=son[1]->size;
}
}*root[N];
inline bool connection(tree *p)
{
if (p&&p->fa)return p->fa->son[1]==p;
}
inline void connect(tree *&p,tree *&fa,bool which)
{
if (p)p->fa=fa;
if (fa)fa->son[which]=p;
}
inline void rotate(tree *&p)
{
tree *fa=p->fa;
bool lr=connection(p);
connect(p,fa->fa,connection(fa));
connect(p->son[lr^1],fa,lr);
connect(fa,p,lr^1);
fa->pushup();p->pushup();
}
inline void splay(tree *&p,tree *&root)
{
for (tree *fa;fa=p->fa;rotate(p))
if (fa->fa)
rotate(connection(p)==connection(fa)?fa:p);
root=p;
}
inline void insert(int insertion,tree *&root)
{
if (root==NULL)
{
root=new tree(insertion);
return;
}
tree *now=root;
while (1)
{
if (now->value==insertion)return;
tree *fa=now;
bool direction=fa->value<insertion;
now=fa->son[direction];
if (now==NULL)
{
now=new tree(insertion);
connect(now,fa,direction);
fa->pushup();
splay(now,root);
return;
}
}
}
inline int find_rank(int ranking,tree *root)
{
tree *now=root;
if (root->size<ranking)return 0;
while (1)
if (now->son[0]&&ranking<=now->son[0]->size)
now=now->son[0];
else
{
ranking-=(now->son[0]?now->son[0]->size:0)+1;
if (!ranking)return now->value;
now=now->son[1];
}
}
void merge(tree *&p,tree *&root)
{
if (p->son[0])merge(p->son[0],root);
if (p->son[1])merge(p->son[1],root);
insert(p->value,root);
}
}using namespace Splay;
namespace Set //并查集
{
int fa[N];
int find(int p)
{
return fa[p]==p?p:fa[p]=find(fa[p]);
}
inline void Union(int a,int b)
{
int f1=find(a),f2=find(b);
if (f1==f2)return;
if (root[f1]->size>root[f2]->size)std::swap(f1,f2);
//默认小树合大树
fa[f1]=f2;
merge(root[f1],root[f2]);
}
}
int n,m,q,num[N]={-1}; //num[x]表示重要值为x的岛屿的编号,注意-1
int main()
{
n=read();m=read();
for (int i=1;i<=n;i++)
{
Set::fa[i]=i; //记得初始化
int ranking=read();
num[ranking]=i;
insert(ranking,root[i]);
}
while (m--)Set::Union(read(),read());
q=read();
while (q--)
{
char opt;scanf(" %c",&opt);
int x=read(),y=read();
if (opt==66)Set::Union(x,y);
else printf("%d\n",num[find_rank(y,root[Set::find(x)])]); //注意是find(x) 需要访问集合的根
}
}