BZOJ2733:使用并查集维护连通性之后用线段树维护+线段树合并(动态开点)

可以说是线段树合并的裸题吧

题意就是给你两个操作

一个操作是合并两个集合,这两个集合都是用权值线段树维护的,便于查询第k小元素

另一个操作就是查询区间极值了

 1 #include<cstdio>
 2 const int maxn=100005;
 3 int n,m,sz;
 4 int v[maxn],id[maxn],fa[maxn],root[maxn];
 5 int lch[1800000],rch[1800000],sum[1800000];
 6 inline int read()
 7 {
 8     int x=0,f=1;char ch=getchar();
 9     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
10     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
11     return x*f;
12 }
13 int find(int x)
14 {
15     return x==fa[x]?x:fa[x]=find(fa[x]);
16 }
17 void insert(int &k,int l,int r,int val)
18 {
19     if(k==0) k=++sz;
20     if(l==r)
21     {
22         sum[k]=1;return;
23     }
24     int mid=(l+r)>>1;
25     if(val<=mid) insert(lch[k],l,mid,val);
26     else insert(rch[k],mid+1,r,val);
27     sum[k]=sum[lch[k]]+sum[rch[k]];
28 }
29 int query(int k,int l,int r,int rank)
30 {
31     if(l==r) return l;
32     int mid=(l+r)>>1;
33     if(sum[lch[k]]>=rank) return query(lch[k],l,mid,rank);
34     else return query(rch[k],mid+1,r,rank-sum[lch[k]]);
35 }
36 int merge(int x,int y)
37 {
38     if(x==0) return y;
39     if(y==0) return x;
40     lch[x]=merge(lch[x],lch[y]);
41     rch[x]=merge(rch[x],rch[y]);
42     sum[x]=sum[lch[x]]+sum[rch[x]];
43     return x;
44 }
45 int main()
46 {
47     n=read();m=read();
48     for(int i=1;i<=n;i++) v[i]=read();
49     for(int i=1;i<=n;i++) fa[i]=i;
50     int x,y;
51     for(int i=1;i<=m;i++)
52     {
53         x=read(),y=read();
54         int p=find(x),q=find(y);
55         fa[p]=q;
56     }
57     for(int i=1;i<=n;i++)
58     {
59         insert(root[find(i)],1,n,v[i]);  //往对应的线段树插点
60         id[v[i]]=i; 
61     }
62     int k=read();
63     char ch[2];
64     while(k--)
65     {
66         scanf("%s",ch);
67         x=read();y=read();
68         if(ch[0]=='Q')
69         {
70             int p=find(x);
71             if(sum[root[p]]<y)
72             {
73                 puts("-1");continue;//查询越界 
74             }
75             int t=query(root[p],1,n,y);  //得到location 
76             printf("%d
",id[t]);
77         }
78         else
79         {
80             int p=find(x),q=find(y);
81             if(p!=q)
82             {
83                 fa[p]=q;
84                 root[q]=merge(root[p],root[q]);
85             }
86         } 
87     }
88     return 0;
89 }

权值线段树的理解更加深刻了

权值线段树的下标是数字本身,而存的是这个数出现的次数,也就是权值

一般权值线段树都是和动态开点捆绑在一起的

所谓动态开点,就是每个节点用的时候再开,可以去掉许多无用的节点

和主席树的区别,目前阶段的理解就是,主席树需要离散化,动态开点线段树不需要?

建n棵线段树,每一棵线段树维护[1,i]的数字出现情况

也就是当前数字范围内的数出现了多少次

然后前缀和查找就好了

可以这么说,动态开点的权值线段树的儿子之间没有耦合,可持久化权值线段树的儿子之间是耦合在一起的

虽然功能一样的,但是T和M会有差异