線段樹合併&&啓發式合併筆記

這倆東西聽起來很高端,實際上很好寫,應用也不少~node

線段樹合併

線段樹合併,顧名思義,就是創建一棵新的線段樹保存原有的兩顆線段樹的信息。ios

考慮如何合併,對於一個結點,若是兩顆線段樹都有此位置的結點,則直接合並兩結點的信息(如維護最大值則取max,維護和則相加),而後遞歸處理左右子樹;數據結構

若只有一個有,直接返回便可。spa

這樣子作時間複雜度取決於重合節點個數,一次最壞複雜度是$O(nlogn)$,由於滿二叉樹的結點數是$O(n)$,對每一個結點進行處理是$O(logn)$,可是實際應用中須要合併的兩顆樹重合部分通常較少,因此複雜度能夠近似看爲$O(logn)$的;code

若是用動態開點線段樹的話,一次合併只須要合併一條鏈,因此時間複雜度是$O(操做數\times logn)$的blog

啓發式合併

啓發式合併核心思想就一句話:把小集合的合併到大的裏。遞歸

啓發式合併思想能夠放到不少數據結構裏,鏈表、線段樹、甚至平衡樹均可以。string

考慮時間複雜度,設總共有$n$個元素,因爲每次集合的大小至少翻倍,因此至多會合並$logn$次,總的複雜度就是$O(nlogn)$的(結合線段樹合併就是$O(nlog^2n)$的)it

下面舉幾道例題:io

【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

相關文章
相關標籤/搜索