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


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

线段树合并

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

考虑如何合并,对于一个结点,如果两颗线段树都有此位置的结点,则直接合并两结点的信息(如维护最大值则取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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM