csu 1949 小明的烦恼 并查集+权值线段树

来源:互联网 发布:淘宝联盟赚钱是真的吗 编辑:程序博客网 时间:2024/06/06 02:30

题目链接


思路:

           首先,对于每个员工之间的合并,可以采用并查集来解决这件事.那么主要的问题就是如何求这个集合中第k大的数.这里采用权值线段树来合并,大体的权值线段树,就是我们当时用来求逆序数的用法。

  题目中是2e5的数据范围,那么我们初始对每个员工建立一个树,然后记录下他的根节点和他的左右子节点,合并时,将两个团队所在树的根节点的合并就好,

#include<bits/stdc++.h>#define inf 0x3f3f3f3fusing namespace std;const int maxn=1e5+10;const int maxm=1e7+10;struct node{int ls,rs,sum;int l,r;}q[maxm];int a[maxn],father[maxn],root[maxn],s[maxn];int n,m;int k;int build(int l,int r,int d)//建树{k++;int t=k;q[t].l=l,q[t].r=r;if(r<d||l>d){q[t].ls=0,q[t].rs=0,q[t].sum=0;return t;}else{q[t].sum=1;if(l==r){q[t].ls=0,q[t].rs=0;   return t;}int mid=(l+r)>>1;q[t].ls=build(l,mid,d);q[t].rs=build(mid+1,r,d);return t;}} int find(int x){return x==father[x]?x:father[x]=find(father[x]); } int get(int roots,int ss)//找第k大的数{if(q[roots].l==q[roots].r)return q[roots].l;int ls=q[roots].ls,rs=q[roots].rs;if(q[ls].sum<ss)return get(rs,ss-q[ls].sum);elsereturn get(ls,ss);}int merge(int x,int y)//合并{if(q[x].ls==0)q[x].ls=q[y].ls;else{if(q[y].ls>0){q[x].ls=merge(q[x].ls,q[y].ls);}}if(q[x].rs==0)q[x].rs=q[y].rs;else{if(q[y].rs>0){q[x].rs=merge(q[x].rs,q[y].rs);}}q[x].sum+=q[y].sum;return x;}int main(){while(scanf("%d %d",&n,&m)!=EOF){int maxl=inf,maxr=0;for(int i=1;i<=n;i++){ scanf("%d",&a[i]); maxl=min(maxl,a[i]); maxr=max(maxr,a[i]);}k=0;for(int i=1;i<=n;i++){father[i]=i;s[i]=1;root[i]=build(maxl,maxr,a[i]);}while(m--){int x,y;int cnd;scanf("%d",&cnd);if(cnd==2){scanf("%d %d",&x,&y);int fx=find(x),fy=find(y);s[fx]+=s[fy];father[fy]=fx;root[fx]=merge(root[fx],root[fy]);}else{scanf("%d",&x);int fx=find(x);printf("%d\n",get(root[fx],(s[fx]+1)>>1));}}}return 0;}


原创粉丝点击