1. 程式人生 > >【啟發式合併】線段樹,平衡樹

【啟發式合併】線段樹,平衡樹

【啟發式合併】線段樹,平衡樹

啟發式合併就是一種複雜度可以證明的貪心合併

平衡樹啟發式合併:

對於平衡樹的啟發式合併,我們將一個 $size$ 較小平衡樹一個一個結點暴力加入 $size$ 較大的平衡樹中

最壞時間複雜度是玄學的 $O(N log^{2} N)$

空間複雜度 $O(N)$

模板題:P3224 [HNOI2012]永無鄉

  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\n",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)$

模板題:P4556 [Vani有約會]雨天的尾巴

  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\n",ans[i]);
127     return 0;
128 }

 

堆的啟發式合併

由於左偏樹更加穩定,所以通常用不上