【启发式合并】线段树,平衡树
启发式合并就是一种复杂度可以证明的贪心合并
平衡树启发式合并:
对于平衡树的启发式合并,我们将一个 $size$ 较小平衡树一个一个结点暴力加入 $size$ 较大的平衡树中
最坏时间复杂度是玄学的 $O(N log^{2} N)$
空间复杂度 $O(N)$
1 #include<bits/stdc++.h> 2 #define MAXN 100010 3 using namespace std; 4 inline int read () 5 { 6 int s=0,w=1; 7 char ch=getchar (); 8 while (ch<'0'||ch>'9'){if (ch=='-') w=-1;ch=getchar ();} 9 while ('0'<=ch&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar (); 10 return s*w; 11 } 12 int n,m,q; 13 int ch[MAXN][2],fa[MAXN],rk[MAXN]; 14 int Fa[MAXN],root[MAXN],size[MAXN]; 15 char opt[10]; 16 int find (int x) 17 { 18 if (Fa[x]!=x) Fa[x]=find (Fa[x]); 19 return Fa[x]; 20 } 21 int get (int x) 22 { 23 return ch[fa[x]][1]==x; 24 } 25 void update (int x) 26 { 27 size[x]=size[ch[x][0]]+size[ch[x][1]]+1; 28 } 29 void rotate (int x) 30 { 31 int y=fa[x],z=fa[y],k=get (x); 32 if (z) ch[z][get (y)]=x;fa[x]=z; 33 ch[y][k]=ch[x][k^1]; 34 if (ch[x][k^1]) fa[ch[x][k^1]]=y; 35 ch[x][k^1]=y;fa[y]=x; 36 update (y),update (x); 37 } 38 void splay (int w,int x,int pos) 39 { 40 while (fa[x]!=pos) 41 { 42 int y=fa[x]; 43 if (fa[y]!=pos) rotate (get (x)==get (y)?y:x); 44 rotate (x); 45 } 46 if (pos==0) root[w]=x; 47 } 48 void insert (int w,int k) 49 { 50 int ff,x=root[w]; 51 while (x) 52 { 53 ff=x; 54 if (rk[k]<rk[x]) x=ch[x][0]; 55 else x=ch[x][1]; 56 } 57 ch[ff][rk[k]>rk[ff]]=k,fa[k]=ff; 58 splay (w,k,0); 59 } 60 void merge (int w,int x) 61 { 62 if (!x) return; 63 merge (w,ch[x][0]);merge (w,ch[x][1]); 64 fa[x]=ch[x][0]=ch[x][1]=0; 65 insert (w,x); 66 } 67 int getkth (int w,int k) 68 { 69 int x=root[w]; 70 while (x) 71 { 72 if (size[ch[x][0]]+1==k) return splay (w,x,0),x; 73 else if (size[ch[x][0]]>=k) x=ch[x][0]; 74 else k-=size[ch[x][0]]+1,x=ch[x][1]; 75 } 76 return -1; 77 } 78 int main() 79 { 80 n=read (),m=read (); 81 for (int i=1;i<=n;i++) rk[i]=read (),Fa[i]=i,root[i]=i,size[i]=1; 82 for (int i=1;i<=m;i++) 83 { 84 int u=read (),v=read (); 85 int r1=find (u),r2=find (v); 86 if (r1!=r2) 87 { 88 if (size[root[r1]]>size[root[r2]]) swap (r1,r2); 89 Fa[r1]=r2,merge (r2,root[r1]); 90 } 91 } 92 q=read (); 93 while (q--) 94 { 95 scanf ("%s",opt+1); 96 if (opt[1]=='Q') 97 { 98 int x=read (),k=read (); 99 printf ("%d ",getkth (find (x),k)); 100 } 101 else 102 { 103 int u=read (),v=read (); 104 int r1=find (u),r2=find (v); 105 if (r1!=r2) 106 { 107 if (size[root[r1]]>size[root[r2]]) swap (r1,r2); 108 Fa[r1]=r2,merge (r2,root[r1]); 109 } 110 } 111 } 112 return 0; 113 }
线段树合并
我们使用动态开点线段树,我们按照 $dfs$ 的顺序,将每个子节点并到父节点上。线段树的下标一般就是要维护的值
时间复杂度可以达到 $O(N log N)$,但空间复杂度为 $O(N log N)$
1 #include<bits/stdc++.h> 2 #define MAXN 100010 3 using namespace std; 4 inline int read () 5 { 6 int s=0,w=1; 7 char ch=getchar (); 8 while (ch<'0'||ch>'9'){if (ch=='-') w=-1;ch=getchar ();} 9 while ('0'<=ch&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar (); 10 return s*w; 11 } 12 struct Node{ 13 int pos,val; 14 }; 15 struct SEG{ 16 int l,r,id,num; 17 }tr[MAXN*100]; 18 struct edge{ 19 int v,nxt; 20 }e[MAXN<<1]; 21 int n,m,cnt,len,Maxz; 22 int head[MAXN],ans[MAXN]; 23 int fa[MAXN],root[MAXN],son[MAXN],size[MAXN],top[MAXN],dep[MAXN]; 24 vector<Node>vec[MAXN]; 25 void add (int u,int v) 26 { 27 e[++cnt].v=v; 28 e[cnt].nxt=head[u]; 29 head[u]=cnt; 30 } 31 void dfs1 (int u,int ff) 32 { 33 fa[u]=ff,dep[u]=dep[ff]+1,size[u]=1; 34 for (int i=head[u];i!=0;i=e[i].nxt) 35 if (e[i].v!=ff) 36 { 37 dfs1 (e[i].v,u); 38 size[u]+=size[e[i].v]; 39 if (size[e[i].v]>size[son[u]]) son[u]=e[i].v; 40 } 41 } 42 void dfs2 (int u,int topf) 43 { 44 top[u]=topf; 45 if (son[u]) dfs2 (son[u],topf); 46 for (int i=head[u];i!=0;i=e[i].nxt) 47 if (!top[e[i].v]) 48 dfs2 (e[i].v,e[i].v); 49 } 50 int LCA (int x,int y) 51 { 52 while (top[x]!=top[y]) 53 { 54 if (dep[top[x]]<dep[top[y]]) swap (x,y); 55 x=fa[top[x]]; 56 } 57 return dep[x]<dep[y]?x:y; 58 } 59 void pushup (int x) 60 { 61 int l=tr[x].l,r=tr[x].r; 62 if (tr[l].num>tr[r].num||(tr[l].num==tr[r].num&&tr[l].id<tr[r].id)) 63 tr[x].num=tr[l].num,tr[x].id=tr[l].id; 64 else tr[x].num=tr[r].num,tr[x].id=tr[r].id; 65 } 66 void update (int &x,int l,int r,int pos,int val) 67 { 68 if (!x) x=++len; 69 if (l==r) 70 { 71 tr[x].id=pos,tr[x].num+=val; 72 return; 73 } 74 int mid=(l+r)>>1; 75 if (pos<=mid) update (tr[x].l,l,mid,pos,val); 76 else update (tr[x].r,mid+1,r,pos,val); 77 pushup (x); 78 } 79 int merge (int a,int b,int l,int r) 80 { 81 if (!a||!b) return a+b; 82 if (l==r) 83 { 84 tr[a].num+=tr[b].num; 85 return a; 86 } 87 int mid=(l+r)>>1; 88 tr[a].l=merge (tr[a].l,tr[b].l,l,mid); 89 tr[a].r=merge (tr[a].r,tr[b].r,mid+1,r); 90 pushup (a); 91 return a; 92 } 93 void dfs (int u,int ff) 94 { 95 for (int i=0;i<vec[u].size ();i++) 96 update (root[u],1,Maxz,vec[u][i].pos,vec[u][i].val); 97 for (int i=head[u];i!=0;i=e[i].nxt) 98 if (e[i].v!=ff) 99 { 100 dfs (e[i].v,u); 101 root[u]=merge (root[u],root[e[i].v],1,Maxz); 102 } 103 if (tr[root[u]].num==0) ans[u]=0; 104 else ans[u]=tr[root[u]].id; 105 } 106 int main() 107 { 108 n=read (),m=read (); 109 for (int i=1;i<n;i++) 110 { 111 int u=read (),v=read (); 112 add (u,v);add (v,u); 113 } 114 dfs1 (1,0); 115 dfs2 (1,1); 116 while (m--) 117 { 118 int x=read (),y=read (),z=read (),lca=LCA (x,y); 119 Maxz=max (Maxz,z); 120 if (lca==x) vec[fa[x]].push_back (Node{z,-1}),vec[y].push_back (Node{z,1}); 121 else if (lca==y) vec[fa[y]].push_back (Node{z,-1}),vec[x].push_back (Node{z,1}); 122 else vec[fa[lca]].push_back (Node{z,-1}),vec[lca].push_back (Node{z,-1}),vec[x].push_back (Node{z,1}),vec[y].push_back (Node{z,1}); 123 } 124 dfs (1,0); 125 for (int i=1;i<=n;i++) 126 printf ("%d ",ans[i]); 127 return 0; 128 }
堆的启发式合并
由于左偏树更加稳定,所以通常用不上