题目描述:
每个小岛有一个重要度W
资瓷以下操作:
1:在 X Y 之间建一座桥梁
2:查询与 X 联通的小岛中 重要度为第K小的岛
题目分析:
看到建边就开始乱搞LCT了,雾
其实连边我们可以搞一下启Fa♂式合并(巨巨们说是一个log
1.Splay+启Fa♂式合并
我们刚开始把每个节点都当成一个独立的Splay
然后用一下并查集维护一下联通关系就好了
O(Qlogn2)
O
(
Q
l
o
g
n
2
)
2.权值线段树+启Fa♂式合并
对于每个节点建立一颗权值线段树,然后启发式合并线段树就好了。
也是用并查集维护联通关系
O(Qlogn2)
O
(
Q
l
o
g
n
2
)
我的线段树写的有点假,应该是细节处理的不好,跑的比平衡树慢一点
题目链接:
COGS 1341
Bzoj 2733
Luogu 3224
Ac 代码:
Splay
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
const int maxm=110000;
int rt[maxm];
int fa[maxm],ch[maxm][2],siz[maxm];
int w[maxm];
int Fa[maxm];
std::queue<int> dl;
inline int find(int x){return Fa[x]==x?x:Fa[x]=find(Fa[x]);}
inline bool get(int x){return (ch[fa[x]][1]==x);}
inline void pushup(int x)
{
if(!x) return;
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
}
inline void rotate(int x)
{
int f1=fa[x],f2=fa[f1];
bool g1=get(x),g2=get(f1);
ch[f1][g1]=ch[x][g1^1];
fa[ch[x][g1^1]]=f1;
ch[x][g1^1]=f1,fa[f1]=x;
fa[x]=f2;
if(f2) ch[f2][g2]=x;
pushup(f1),pushup(x);
}
inline void splay(int i,int x,int top)
{
for(int fax;(fax=fa[x])!=top;rotate(x))
if(fa[fax]!=top) rotate(get(fax)==get(x)?fax:x);
if(!top) rt[i]=x;
}
inline void insert(int x,int &now,int fat)
{
if(!now)
{
now=x;
fa[now]=fat;
return;
}
siz[now]++;
if(w[x]<=w[now]) insert(x,ch[now][0],now);
else insert(x,ch[now][1],now);
}
inline void merge(int x,int y)
{
if(x==y) return;
if(siz[rt[x]]>siz[rt[y]]) std::swap(x,y);
Fa[x]=y;
dl.push(rt[x]);
while(!dl.empty())
{
int now=dl.front();
dl.pop();
if(ch[now][0]) dl.push(ch[now][0]);
if(ch[now][1]) dl.push(ch[now][1]);
insert(now,rt[y],0);
splay(y,now,0);
}
}
inline int find_kth(int i,int rank)
{
int now=rt[i];
if(siz[now]<rank) return -1;
while(1)
{
if(rank<=siz[ch[now][0]]) now=ch[now][0];
else if(siz[ch[now][0]]+1==rank) return now;
else rank-=siz[ch[now][0]]+1,now=ch[now][1];
}
}
int main()
{
//freopen("bzoj_2733.in","r",stdin);
//freopen("bzoj_2733.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
rt[i]=i,siz[i]=1,Fa[i]=i;
}
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
merge(find(u),find(v));
}
int q;
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
char s[10];
scanf("%s",s);
if(s[0]=='Q')
{
int x,k;
scanf("%d%d",&x,&k);
printf("%d\n",find_kth(find(x),k));
}
else
{
int u,v;
scanf("%d%d",&u,&v);
merge(find(u),find(v));
}
}
return 0;
}
权值线段树
#include <cstdio>
#include <iostream>
#include <algorithm>
const int maxm=1e6+100;
int fa[maxm];
int sum[maxm<<2],ls[maxm<<2],rs[maxm<<2],rt[maxm];
int pos[maxm];
int sz;
int n,m,q;
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void pushup(int x){sum[x]=(sum[ls[x]]+sum[rs[x]]);}
void insert(int &o,int l,int r,int ind)
{
//printf("%d %d\n",l,r);
if(!o) o=++sz;
if(l>=r)
{
sum[o]++;
return;
}
int mid=(l+r)>>1;
if(ind<=mid) insert(ls[o],l,mid,ind);
else insert(rs[o],mid+1,r,ind);
pushup(o);
}
int merge(int o,int pre)
{
if(!o||!pre) return o+pre;
ls[o]=merge(ls[o],ls[pre]);
rs[o]=merge(rs[o],rs[pre]);
pushup(o);
return o;
}
int kth(int o,int l,int r,int rank)
{
if(l>=r) return l;
int mid=(l+r)>>1;
if(rank<=sum[ls[o]]) return kth(ls[o],l,mid,rank);
else return kth(rs[o],mid+1,r,rank-sum[ls[o]]);
}
int main()
{
//freopen("bzoj_2733.in","r",stdin);
//freopen("bzoj_2733.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int w;
scanf("%d",&w);
pos[w]=i,fa[i]=i;
insert(rt[i],1,n,w);
}
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
int f1=find(u),f2=find(v);
if(f1==f2) continue;
fa[f1]=f2;
rt[f2]=merge(rt[f1],rt[f2]);
}
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
char s[10];
scanf("%s",s);
if(s[0]=='Q')
{
int x,k;
scanf("%d%d",&x,&k);
if(sum[rt[find(x)]]<k) printf("-1\n");
else printf("%d\n",pos[kth(rt[find(x)],1,n,k)]);
}
else
{
int u,v;
scanf("%d%d",&u,&v);
int f1=find(u),f2=find(v);
if(f1==f2) continue;
fa[f1]=f2;
rt[f2]=merge(rt[f1],rt[f2]);
}
}
return 0;
}