【启发式合并】线段树,平衡树

【启发式合并】线段树,平衡树

启发式合并就是一种复杂度可以证明的贪心合并

平衡树启发式合并:

对于平衡树的启发式合并,我们将一个 $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 }

堆的启发式合并

由于左偏树更加稳定,所以通常用不上

猜你喜欢

转载自www.cnblogs.com/PaulShi/p/10081749.html