ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[HNOI2012]永无乡

2021-10-27 17:00:46  阅读:168  来源: 互联网

标签:return int 永无 合并 sum HNOI2012 权值 线段


[HNOI2012]永无乡

题意:

一共 \(n\) 个点,每个点权值,给你 \(q\) 个操作:

  • B x y 表示连接 \(x,y\)
  • Q x k 表示求当前 \(x\) 所在连通块内权值第 \(k\) 小的点的编号

分析:

求一个联通块内的权值第 \(k\) 小的点,很容易想到主席树或者权值线段树,但是考虑到有合并连通块的操作,因此可以确定写法:

权值线段树+线段树合并

代码:

考虑记录信息:正常的权值线段树只用记录数的个数,但是我们还要输出编号,因此用一个 \(id\) 数组记录这个点对应的编号。

首先,先建立权值线段树:

int add(int x,int l,int r,int pos,int c){
    if(!x) x=++cnt;
    if(l==r){
        t[x].id=c; t[x].sum++; return x;
    }
    int mid=l+r>>1;
    if(pos<=mid) t[x].ls=add(t[x].ls,l,mid,pos,c);
    else t[x].rs=add(t[x].rs,mid+1,r,pos,c);
    push_up(x);
    return x;
}
.....
for(int i=1,x;i<=n;i++){
    fa[i]=i; scanf("%d",&x); rt[i]=add(rt[i],1,n,x,i);
}

不像正常的直接建立的权值线段树,我们需要记录这个节点所对应的编号,这样合并时才不会出问题。

然后,对于每一个建边操作,我们都进行一次线段树合并:

找到 \(x\) ,\(y\) 所在连通块的根 \(fx,fy\) ,合并这两个根代表的编号的连通块,即是符合要求的合并

询问建边和预建边的操作是一样的。当然,两者在同一个连通块内就不用管了

int merge(int p,int q,int l,int r){//合并线段树要和合并树状数组方向一致
    if(!p) return q;
    if(!q) return p;
    if(l==r){
        t[p].id=t[q].id; t[p].sum+=t[q].sum;
        return p;
    }
    int mid=l+r>>1;
    t[p].ls=merge(t[p].ls,t[q].ls,l,mid);
    t[p].rs=merge(t[p].rs,t[q].rs,mid+1,r);
    push_up(p);
    return p;
}
····
for(int i=1,x,y;i<=m;i++){
    scanf("%d%d",&x,&y); x=get(x),y=get(y);
    fa[y]=x;
    rt[x]=merge(rt[x],rt[y],1,n);
}

查询时,就是正常的查询 \(x\) 的根节点的权值线段树的第 \(k\) 小值,这是权值线段树的基本操作。

int query(int x,int l,int r,int k){
    if(t[x].sum<k||!x) return 0;//没这么多数或者没这个点
    if(l==r) return t[x].id;
    int mid=l+r>>1,ans;
    if(k<=t[t[x].ls].sum) ans=query(t[x].ls,l,mid,k);
    else ans=query(t[x].rs,mid+1,r,k-t[t[x].ls].sum);
    return ans;
}
·····
else{
    scanf("%d%d",&x,&y); x=get(x);
    int ans=query(rt[x],1,n,y);
    if(!ans) puts("-1");
    else cout<<ans<<endl;
}

整体代码在这里:My Luogu

标签:return,int,永无,合并,sum,HNOI2012,权值,线段
来源: https://www.cnblogs.com/guanlexiangfan/p/15471577.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有