线段树合并&&启发式合并笔记

这俩东西听起来很高端,实际上很好写,应用也很多~

线段树合并

线段树合并,顾名思义,就是建立一棵新的线段树保存原有的两颗线段树的信息。

考虑如何合并,对于一个结点,如果两颗线段树都有此位置的结点,则直接合并两结点的信息(如维护最大值则取max,维护和则相加),然后递归处理左右子树;

若只有一个有,直接返回即可。

这样子做时间复杂度取决于重合节点个数,一次最坏复杂度是$O(nlogn)$,因为满二叉树的结点数是$O(n)$,对每个结点进行处理是$O(logn)$,但是实际应用中需要合并的两颗树重合部分一般较少,所以复杂度可以近似看为$O(logn)$的;

如果用动态开点线段树的话,一次合并只需要合并一条链,所以时间复杂度是$O(操作数\times logn)$的

启发式合并

启发式合并核心思想就一句话:把小集合的合并到大的里。

启发式合并思想可以放到很多数据结构里,链表、线段树、甚至平衡树都可以。

考虑时间复杂度,设总共有$n$个元素,由于每次集合的大小至少翻倍,所以至多会合并$logn$次,总的复杂度就是$O(nlogn)$的(结合线段树合并就是$O(nlog^2n)$的)

下面举几道例题:

【BZOJ1483】【HNOI2009】梦幻布丁

链表+启发式合并,每次换颜色直接合并

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cmath>
 5 using namespace std;
 6 int n,m,s,x,y,ans=0,a[1000001],lsh[1000001],head[1000001],nxt[1000001],sum[1000001],pre[1000001];
 7 void work(int x,int y){
 8     for(int tmp=head[x];tmp!=-1;tmp=nxt[tmp]){
 9         if(a[tmp+1]==y)ans--;
10         if(a[tmp-1]==y)ans--;
11     }
12     for(int tmp=head[x];tmp!=-1;tmp=nxt[tmp])a[tmp]=y;
13     nxt[lsh[x]]=head[y];
14     head[y]=head[x];
15     sum[y]+=sum[x];
16     head[x]=-1;
17     lsh[x]=sum[x]=0;
18 }
19 int main(){
20     memset(sum,0,sizeof(sum));
21     memset(pre,0,sizeof(pre));
22     memset(head,-1,sizeof(head));
23     scanf("%d%d",&n,&m);
24     for(int i=1;i<=n;i++){
25         scanf("%d",&a[i]);
26         pre[a[i]]=a[i];
27         if(a[i]!=a[i-1])ans++;
28         if(head[a[i]]==-1)lsh[a[i]]=i;
29         sum[a[i]]++;
30         nxt[i]=head[a[i]];
31         head[a[i]]=i;
32     }
33     for(int i=1;i<=m;i++){
34         scanf("%d",&s);
35         if(s==2)printf("%d\n",ans);
36         else{
37             scanf("%d%d",&x,&y);
38             if(x==y)continue;
39             if(sum[pre[x]]>sum[pre[y]])swap(pre[x],pre[y]);
40             x=pre[x],y=pre[y];
41             if(!sum[x])continue;
42             work(x,y);
43         }
44     }
45     return 0;
46 }

【BZOJ3123】【SDOI2013】森林

主席树+启发式合并

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<cmath>
  6 #define N 1000000000
  7 using namespace std;
  8 typedef long long ll;
  9 struct edge{
 10     int v,next;
 11 }a[2000001];
 12 int DYZ_HAS_CHANCE,n,m,t,u,v,w,ans=0,tot=0,cnt=0,rt[1000001],ls[5000001],rs[5000001],siz[5000001],sum[1000001],f[1000001],num[1000001],fa[1000001][20],dep[1000001],head[1000001];
 13 bool used[1000001];
 14 char s[10];
 15 int ff(int u){
 16     return f[u]==u?u:f[u]=ff(f[u]);
 17 }
 18 void add(int u,int v){
 19     a[++tot].v=v;
 20     a[tot].next=head[u];
 21     head[u]=tot;
 22 }
 23 int lca(int u,int v){
 24     if(dep[u]<dep[v])swap(u,v);
 25     int l=dep[u]-dep[v];
 26     for(int i=19;i>=0;i--){
 27         if((1<<i)&l)u=fa[u][i];
 28     }
 29     if(u==v)return u;
 30     for(int i=19;i>=0;i--){
 31         if(fa[u][i]!=fa[v][i]){
 32             u=fa[u][i],v=fa[v][i];
 33         }
 34     }
 35     return fa[u][0];
 36 }
 37 void updata(int k,int &now,int l,int r,int v){
 38     if(!now)now=++cnt;
 39     siz[now]=siz[k]+1;
 40     if(l==r)return;
 41     int mid=(l+r)/2;
 42     if(v<=mid)rs[now]=rs[k],updata(ls[k],ls[now],l,mid,v);
 43     else ls[now]=ls[k],updata(rs[k],rs[now],mid+1,r,v);
 44 }
 45 int query(int a1,int a2,int a3,int a4,int l,int r,int v){
 46     if(l==r)return l;
 47     int mid=(l+r)/2,ret=siz[ls[a1]]+siz[ls[a2]]-siz[ls[a3]]-siz[ls[a4]];
 48     if(v<=ret)return query(ls[a1],ls[a2],ls[a3],ls[a4],l,mid,v);
 49     else return query(rs[a1],rs[a2],rs[a3],rs[a4],mid+1,r,v-ret);       
 50 }
 51 void dfs(int u,int f){
 52     used[u]=true;
 53     dep[u]=dep[f]+1;
 54     fa[u][0]=f;
 55     for(int i=1;i<=19;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
 56     updata(rt[f],rt[u],0,N,num[u]);
 57     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
 58         int v=a[tmp].v;
 59         if(v!=f)dfs(v,u);
 60     }
 61 }
 62 void merge(int u,int v){
 63     int u1=ff(u),v1=ff(v);
 64     f[u1]=v1;
 65     sum[v1]+=sum[u1];
 66 }
 67 int main(){
 68     memset(head,-1,sizeof(head));
 69     memset(used,0,sizeof(used));
 70     memset(rt,0,sizeof(rt));
 71     scanf("%d",&DYZ_HAS_CHANCE);
 72     scanf("%d%d%d",&n,&m,&t);
 73     for(int i=1;i<=n;i++){
 74         scanf("%d",&num[i]);
 75         f[i]=i;
 76         sum[i]=1;
 77     }
 78     for(int i=1;i<=m;i++){
 79         scanf("%d%d",&u,&v);
 80         add(u,v);
 81         add(v,u);
 82         merge(u,v);
 83     }
 84     for(int i=1;i<=n;i++){
 85         if(!used[i])dfs(i,0);
 86     }
 87     for(int i=1;i<=t;i++){
 88         scanf("%s",s);
 89         if(s[0]=='Q'){
 90             scanf("%d%d%d",&u,&v,&w);
 91             u^=ans;
 92             v^=ans;
 93             w^=ans;
 94             int now=lca(u,v);
 95             printf("%d\n",ans=query(rt[u],rt[v],rt[now],rt[fa[now][0]],0,N,w));
 96         }else{
 97             scanf("%d%d",&u,&v);
 98             u^=ans;
 99             v^=ans;
100             int u1=ff(u),v1=ff(v);
101             if(sum[u1]>sum[v1])swap(u,v);
102             add(u,v);
103             add(v,u);
104             merge(u,v);
105             dfs(u,v);
106         }
107     }
108     return 0;
109 }

【BZOJ3545】【ONTAK2010】Peaks

离线,按照困难度从小到大加边,用线段树维护每个联通块,每次合并联通块即可

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 using namespace std;
 7 struct task{
 8     int a,b,c,id,ok;
 9 }a[1000001];
10 struct treenode{
11     int v,ls,rs;
12 }t[5000001];
13 int n,m,q,tot=0,num[100001],_num[100001],fa[100001],rts[100001],ans[500001];
14 bool cmp(task a,task b){
15     return a.c==b.c?a.ok<b.ok:a.c<b.c;
16 }
17 int ff(int u){
18     return u==fa[u]?u:fa[u]=ff(fa[u]);
19 }
20 void updata(int &u,int l,int r,int v){
21     if(!u)u=++tot;
22     t[u].v=1;
23     if(l==r)return;
24     int mid=(l+r)/2;
25     if(v<=mid)updata(t[u].ls,l,mid,v);
26     else updata(t[u].rs,mid+1,r,v);
27 }
28 int query(int u,int l,int r,int p){
29     if(l==r)return l;
30     int mid=(l+r)/2;
31     if(p<=t[t[u].ls].v)return query(t[u].ls,l,mid,p);
32     else return query(t[u].rs,mid+1,r,p-t[t[u].ls].v);
33 }
34 int merge(int u,int v){
35     if(!u||!v)return u|v;
36     if(!t[u].ls&&!t[u].rs){
37         t[u].v+=t[v].v;
38         return u;
39     }
40     t[u].ls=merge(t[u].ls,t[v].ls);
41     t[u].rs=merge(t[u].rs,t[v].rs);
42     t[u].v=t[t[u].ls].v+t[t[u].rs].v;
43     return u;
44 }
45 int main(){
46     scanf("%d%d%d",&n,&m,&q);
47     for(int i=1;i<=n;i++){
48         scanf("%d",&num[i]);
49         _num[i]=num[i];
50         fa[i]=i;
51     }
52     sort(_num+1,_num+n+1);
53     for(int i=1;i<=n;i++){
54         num[i]=lower_bound(_num+1,_num+n+1,num[i])-_num;
55     }
56     for(int i=1;i<=m;i++){
57         scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c);
58         a[i].ok=0;
59     }
60     for(int i=m+1;i<=m+q;i++){
61         scanf("%d%d%d",&a[i].a,&a[i].c,&a[i].b);
62         a[i].ok=1;
63         a[i].id=i-m;
64     }
65     sort(a+1,a+m+q+1,cmp);
66     for(int i=1;i<=n;i++)updata(rts[i],1,n,num[i]);
67     for(int i=1;i<=m+q;i++){
68         if(a[i].ok==0){
69             int u=ff(a[i].a),v=ff(a[i].b);
70             if(u!=v){
71                 fa[u]=v;
72                 rts[v]=merge(rts[u],rts[v]);
73             }
74         }else{
75              int u=ff(a[i].a);
76              if(t[rts[u]].v<a[i].b)ans[a[i].id]=-1;
77              else ans[a[i].id]=_num[query(rts[u],1,n,t[rts[u]].v-a[i].b+1)];
78         }
79     }
80     for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
81     return 0;
82 }

【BZOJ2212】【POI2011】Tree Rotation

直接从下往上线段树合并即可

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 using namespace std;
 7 typedef long long ll;
 8 struct treenode{
 9     ll v;
10     int ls,rs;
11 }t[6000001];
12 int n,tot=0,cnt=1,num[2000001],ls[2000001],rs[2000001],rts[2000001];
13 ll ans=0,ans1,ans2;
14 void read(int u){
15     scanf("%d",&num[u]);
16     if(!num[u]){
17         read(ls[u]=++cnt);
18         read(rs[u]=++cnt);
19     }
20 }
21 void updata(int &u,int l,int r,int v){
22     if(!u)u=++tot;
23     if(l==r){
24         t[u].v=1;
25         return;
26     }
27     int mid=(l+r)/2;
28     if(v<=mid)updata(t[u].ls,l,mid,v);
29     else updata(t[u].rs,mid+1,r,v);
30     t[u].v=t[t[u].ls].v+t[t[u].rs].v;
31 }
32 int merge(int u,int v){
33     if(!u||!v)return u|v;
34     ans1+=(ll)t[t[u].rs].v*t[t[v].ls].v;
35     ans2+=(ll)t[t[u].ls].v*t[t[v].rs].v;
36     t[u].ls=merge(t[u].ls,t[v].ls);
37     t[u].rs=merge(t[u].rs,t[v].rs);
38     t[u].v=t[t[u].ls].v+t[t[u].rs].v;
39     return u;
40 }
41 void dfs(int u){
42     if(!u)return;
43     dfs(ls[u]);
44     dfs(rs[u]);
45     if(!num[u]){
46         ans1=ans2=0;
47         rts[u]=merge(rts[ls[u]],rts[rs[u]]);
48         ans+=min(ans1,ans2);
49     }
50 }
51 int main(){
52     scanf("%d",&n);
53     read(1);
54     for(int i=1;i<=cnt;i++){
55         if(num[i])updata(rts[i],1,n,num[i]);
56     }
57     dfs(1);
58     printf("%lld",ans);
59     return 0;
60 }

现在搞专题沉迷摸鱼,一天平均只有两道题,颓废力max

posted @ 2018-07-13 12:03  DCDCBigBig  阅读(3778)  评论(1编辑  收藏  举报