這倆東西聽起來很高端,實際上很好寫,應用也不少~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
鏈表+啓發式合併,每次換顏色直接合並
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 }
主席樹+啓發式合併
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 }
離線,按照困難度從小到大加邊,用線段樹維護每一個聯通塊,每次合併聯通塊便可
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 }
直接從下往上線段樹合併便可
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