[HNOI2012]永无乡

题意

给出n个岛屿和每个岛屿的重要度排名,初始岛屿间有m座桥,有两种操作:询问与x相连通的岛屿中重要度排名第k小的岛屿编号,不存在输出-1;给x,y岛屿之间建桥。

对于 100% 的数据 n100000,mn,q300000

题解

需要一个可以查询第k小的数据结构,可以使用splay。

考虑怎么合并:暴力就好了。将小的splay一个一个插入大的splay,每个点最多被合并log次。

现在考虑如何维护多个splay,经过sxk大佬的指点:一开始建n个splay,用root[]记录每个splay的根,然后函数传参的时候就记录操作在哪一个splay就好。

然后连通性的话不想写并查集,于是想到记录每个点在哪颗splay,判断就直接看root[belong[x]]是否相同(而且o(1)),belong在插入时维护就好。

在遍历插入时一定是最后插入,因为你插入时会把信息重新清一遍,所以相当于直接把原来他所在的splay的后面节点抛弃了,然后可能就会从新的splay里面遍历,就会GG。

这道题也不用插入最值(虽然我一直不知道最值有啥用)

对于他们再用一个数组记录排名为i的岛屿的编号有点没看懂,因为如果你最初节点编号就是岛屿编号而且之后节点编号又不变的话,完全没必要记录,直接输出节点编号就好了。

然后合并那里因为判断了splay大小涉及swap,所以后面的splay编号就不能写成belong[x],具体看代码

#include<bits/stdc++.h>
using namespace std;

const int maxn=100005;
int n,m,q;
int root[maxn],belong[maxn];//root每颗splay的根,belong 每个点是哪颗splay
struct Splay{
  int fa,size,val,s[2];
}tr[maxn];

template<class T>inline void read(T &x) {
  x=0;char ch=getchar();
  while(!isdigit(ch)) ch=getchar();
  while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
}

void update(int x){
  tr[x].size=tr[tr[x].s[0]].size+tr[tr[x].s[1]].size+1;
}

void connect(int x,int y,int d){
  tr[x].fa=y;
  tr[y].s[d]=x;
}

int get(int x){
  return tr[tr[x].fa].s[1]==x;
}

void rotate(int x){
  int f=tr[x].fa,ff=tr[f].fa;
  int d1=get(x),d2=get(f);
  int cs=tr[x].s[d1^1];
  connect(x,ff,d2);
  connect(f,x,d1^1);
  connect(cs,f,d1);
  update(f);
  update(x);
}

void splay(int x,int go,int id){
  if(go==root[id]) root[id]=x;
  go=tr[go].fa;
  while(tr[x].fa!=go){
    int f=tr[x].fa;
    if(tr[f].fa==go) rotate(x);
    else if(get(x)==get(f)) {rotate(f);rotate(x);}
    else {rotate(x);rotate(x);}
  }
}

void insert(int id,int val,int x){//插入第几颗splay,值,编号
  int now=root[id];
  belong[x]=id;
  if(!now){
    root[id]=x;
    tr[x].size=1;
    tr[x].val=val;
    return ;
  }
  while(324){
    tr[now].size++;
    int o=val>tr[now].val;
    if(!tr[now].s[o]){
      tr[x]=(Splay){now,1,val,{0,0}};
      tr[now].s[o]=x;
      break;
    }
    now=tr[now].s[o];
  }
  splay(x,root[id],id);
}

void dfs(int x,int y){//y节点插入第x个splay
  //printf("--%d--\n",y);
  if(tr[y].s[0]) dfs(x,tr[y].s[0]);
  if(tr[y].s[1]) dfs(x,tr[y].s[1]);
  insert(x,tr[y].val,y);
}

void merge(){
  int x,y;
  read(x);read(y);
  int dx=root[belong[x]],dy=root[belong[y]];
  if(dx==dy) return ;
  if(tr[dx].size<tr[dy].size) swap(dx,dy);
  //putchar(10);
  //printf("%d %d %d--------\n",belong[x],dx,dy);
  dfs(belong[dx],dy);//不能写belong[x],因为上面可能swap了
}

int query(int now,int k){
  while(324){
    if(tr[tr[now].s[0]].size>=k) {now=tr[now].s[0];continue;}
    k-=tr[tr[now].s[0]].size;
    if(k==1) break;
    k--;
    now=tr[now].s[1];
  }
  splay(now,root[belong[now]],belong[now]);
  return now;
}

int main(){
  read(n);read(m);
  for(int i=1;i<=n;i++){
    int x;read(x);
    insert(i,x,i);
  }
  //for(int i=1;i<=n;i++) printf("%d ",belong[i]);
  //putchar(10);
  for(int i=1;i<=m;i++) merge();
  read(q);
  for(int i=1;i<=q;i++){
    char op[2];
    scanf("%s",op);
    if(op[0]=='B') merge();
    else {
      int x,k;
      read(x);read(k);
      int dx=root[belong[x]];
      if(tr[dx].size<k) printf("-1\n");
      else printf("%d\n",query(dx,k));
    }
  }
}
View Code

 

posted @ 2019-08-05 13:23  _JSQ  阅读(257)  评论(0编辑  收藏  举报