B20J_2733_[HNOI2012]永无乡_权值线段树合并

Description:n座岛,编号从1到n,每座岛都有自己的独一无二的重要度,按照重要度可以将这n座岛排名,名次用1到 n来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。现在有两种操作:B x y表示在岛 x与岛y之间修建一座新桥。Q x k表示询问当前与岛 x连通的所有岛中第k重要的是哪座岛,即所有与岛 x连通的岛中重要度排名第 k小的岛是哪座,请你输出那个岛的编号。

对于100%的数据n≤100000,m≤n,q≤300000。

分析:读懂题后发现是一道线段树合并的裸题。Q操作显然是权值线段树求区间第k小元素,B操作是合并。

直接开发现开不下,需要动态开点,一开始要开nlogn个结点。

合并操作:

1 int merge(int x,int y)
2 {
3     if(!x)return y;
4     if(!y)return x;
5     lson[x]=merge(lson[x],lson[y]);
6     rson[x]=merge(rson[x],rson[y]);
7     t[x]=t[lson[x]]+t[rson[x]];
8     return x;
9 }

代码:

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <algorithm>
  4 using namespace std;
  5 const int N=3262145;
  6 int tree[N],lson[N],rson[N],t[N],mp[N],fa[N],idx[N],cnt;
  7 int n,m,k;
  8 char s[10];
  9 int find(int x)
 10 {
 11     return fa[x]==x?x:fa[x]=find(fa[x]);
 12 }
 13 void bt(int l,int r,int val,int &pos)
 14 {
 15     if(pos==0)pos=++cnt;
 16     if(l==r)
 17     {
 18         t[pos]=1;
 19         return ;
 20     }
 21     int mid=l+r>>1;
 22     if(val<=mid)bt(l,mid,val,lson[pos]);
 23     else bt(mid+1,r,val,rson[pos]);
 24     t[pos]=t[lson[pos]]+t[rson[pos]];
 25 }
 26 int merge(int x,int y)
 27 {
 28     if(!x)return y;
 29     if(!y)return x;
 30     lson[x]=merge(lson[x],lson[y]);
 31     rson[x]=merge(rson[x],rson[y]);
 32     t[x]=t[lson[x]]+t[rson[x]];
 33     return x;
 34 }
 35 int query(int l,int r,int k,int pos)
 36 {
 37     if(l==r||k==0)
 38     {
 39         return mp[l];
 40     }
 41     int mid=l+r>>1;
 42     if(t[pos]<k)return -1;
 43     if(t[lson[pos]]>=k)
 44     {
 45         return query(l,mid,k,lson[pos]);
 46     }
 47     else
 48     {
 49         return query(mid+1,r,k-t[lson[pos]],rson[pos]);
 50     }
 51 }
 52 int main()
 53 {
 54     scanf("%d%d",&n,&m);
 55     for(int i=1;i<=n;i++)
 56     {
 57         fa[i]=i;
 58     }
 59     for(int i=1;i<=n;i++)
 60     {
 61         scanf("%d",&idx[i]);
 62         mp[idx[i]]=i;
 63     }
 64     for(int i=1;i<=n;i++)
 65     {
 66         tree[i]=++cnt;
 67         bt(1,n,idx[i],tree[i]);
 68     }
 69     int x,y;
 70     for(int i=1;i<=m;i++)
 71     {
 72         scanf("%d%d",&x,&y);
 73         int dx=find(x),dy=find(y);
 74         if(dx!=dy)
 75         {
 76             fa[dy]=dx;
 77             tree[dx]=merge(tree[dx],tree[dy]);
 78         }
 79     }
 80     scanf("%d",&k);
 81     while(k--)
 82     {
 83         scanf("%s%d%d",s,&x,&y);
 84         int dx=find(x);
 85         if(s[0]==‘Q‘)
 86         {
 87             printf("%d\n",query(1,n,y,tree[dx]));
 88         }
 89         else
 90         {
 91             int dx=find(x),dy=find(y);
 92             if(dx!=dy)
 93             {
 94                 fa[dy]=dx;
 95                 tree[dx]=merge(tree[dx],tree[dy]);
 96             }
 97         }
 98     }
 99 }
100
101 /***************************************************************
102     Problem: 2117
103     User: 20170105
104     Language: C++
105     Result: Accepted
106     Time:644 ms
107     Memory:90164 kb
108 ****************************************************************/

原文地址:https://www.cnblogs.com/suika/p/8412673.html

时间: 2024-11-03 03:46:46

B20J_2733_[HNOI2012]永无乡_权值线段树合并的相关文章

BZOJ_3685_普通van Emde Boas树_权值线段树

Description 设计数据结构支持: 1 x  若x不存在,插入x 2 x  若x存在,删除x 3    输出当前最小值,若不存在输出-1 4    输出当前最大值,若不存在输出-1 5 x  输出x的前驱,若不存在输出-1 6 x  输出x的后继,若不存在输出-1 7 x  若x存在,输出1,否则输出-1 Input 第一行给出n,m 表示出现数的范围和操作个数 接下来m行给出操作 n<=10^6,m<=2*10^6,0<=x<n Output Sample Input 1

bzoj 2733 永无乡 - 并查集 - 线段树

永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输

T107741 【模板】权值线段树合并

题目地址 #include<cstdio> #include<iostream> using namespace std; const int MAXN=1e6; struct Node{ int ls,rs; int val,pos; }tr[MAXN*20*4]; int nodeCnt=0; void insert(int &p,int l,int r,int x,int val){ if(!p)p=++nodeCnt; if(l==r){ tr[p].val+=va

【bzoj2733】[HNOI2012]永无乡 线段树合并

Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k

【bzoj3702】二叉树 权值线段树

神奇的解法 对于每个节点,建出权值线段树 每次查询右子树的权值线段树和左子树的权值线段树,左子树中比右子树小的有多少?右子树比左子树小的有多少?(分别对应不交换的逆序对和交换的逆序对) 将左子树和右子树的权值线段树合并 递归进行这个操作 感觉复杂度很不靠谱,于是想证明一下复杂度 最开始权值线段树共O(nlogn)个节点,最后共O(n)个节点 每次合并两棵树的每个节点都要访问一遍,所以每个节点好像是要访问O(dep[i])次? 但是,合并两棵树后,有些重复的节点被合并到了一起 所以好像有些节点又没

Codeforces 666E Forensic Examination SAM+权值线段树

第一次做这种$SAM$带权值线段树合并的题 然而$zjq$神犇看完题一顿狂码就做出来了 $Orz$ 首先把所有串当成一个串建$SAM$ 我们对$SAM$上每个点 建一棵权值线段树 每个叶子节点表示一个匹配串能到达这个点的子串个数 这样我们对最后的$SAM$的权值线段树按$parent$树合并 询问的时候找到对应的$SAM$上的权值线段树直接查询就好了 具体的操作看代码吧~ #include<bits/stdc++.h> using namespace std; #define FO(x) {f

HNOI2012永无乡

[HNOI2012]永无乡 Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x

BZOJ 2733: [HNOI2012]永无乡(treap + 启发式合并 + 并查集)

不难...treap + 启发式合并 + 并查集 搞搞就行了 ---------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep(i, n) for(int i = 0; i &l

bzoj2733: [HNOI2012]永无乡(splay)

2733: [HNOI2012]永无乡 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3778  Solved: 2020 Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两