[HNOI 2012] 永无乡

59 篇文章 0 订阅
13 篇文章 0 订阅

题目描述:

每个小岛有一个重要度W
资瓷以下操作:
1:在 X Y 之间建一座桥梁
2:查询与 X 联通的小岛中 重要度为第K小的岛

题目分析:

看到建边就开始乱搞LCT了,雾
其实连边我们可以搞一下启Fa♂式合并(巨巨们说是一个log
1.Splay+启Fa♂式合并
我们刚开始把每个节点都当成一个独立的Splay
然后用一下并查集维护一下联通关系就好了
OQlogn2 O ( Q l o g n 2 )
2.权值线段树+启Fa♂式合并
对于每个节点建立一颗权值线段树,然后启发式合并线段树就好了。
也是用并查集维护联通关系
OQlogn2 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值