zoukankan      html  css  js  c++  java
  • 虚树学习笔记

    虚树算法其实原理蛮简单的就是,从一颗n个结点的原树上在只取出必要结点成一颗新树,这颗新树必包含指定m个结点并保持原树上的祖孙关系。

    首先我们来解答一些问题

    问:什么样的结点是必要的呢??

    答:指定的m个结点和 这m个结点中任意两个结点的最近公共祖先。

    问:为啥要包含最近公共祖先呢?

    答:因为最近公共是虚树的关节点,起到连接这m个点的作用

    问:任意两个结点的公共祖先构成集合,会不会有m*(m-1)/2个点?

    答:其实这些祖先构成的集合最多就m-1个点,因为虚树其实原理有点类似在树上跑不压缩路径的并查集,你连接m个点最多也只需要m-1个关节点.

    首先来看两张图,直观感受虚树的构建

    1.构建必须包含2,4,7,6的虚树

    2.构建必须包含2,7,8的虚树

     虚树的构建方法可以看这篇文章https://www.cnblogs.com/chenhuan001/p/5639482.html

    总体上就是先按dfs排个序,然后用栈加LCA对这m个结点实现深度优先遍历并记录路径以方便建虚树。最后要注意的是建边一定要在出栈的时候建,因为有可能出现图2的那种情况,两个点之间还需再塞进去一个结点。

    算法复杂度:

    因为虚树构建时需要对结点按dfs序排序,并求出排序后相邻结点的LCA的所以算法复杂度O(mlog(n*m))

    虚树优点:

    虚树主要配合树形DP食用,因为树形DP的复杂是与树的结点数成正比的,通过构建虚树,可以把树的结点数下降到2m-1以内,从而大大提高树形DP的效率。

    代码实现:

      1 #include<stdio.h>
      2 #include<string.h>
      3 #include<algorithm>
      4 #include<queue>
      5 #include<vector>
      6 #include<stack>
      7 using namespace std;
      8 const int MAXN=100006;
      9 /**
     10 @var d[v]    结点v的深度
     11 @var f[v][i] 结点v的第2^i个祖先
     12 @var id[v]   结点的dfs序
     13 @var e[v]    结点v在原树邻接表
     14 @var vt[v]   结点v在虚树上包含的邻接表
     15 @var que     要查询的结点的集合(虚树必须包含的结点)
     16 @var LN      log2(最大深度)的向下取值
     17 @var st      栈,用于构建ST表
     18 @var z       dfs序的计数器
     19 注意结点标号必须从1开始,因为0拿去当空结点了。
     20 */
     21 int d[MAXN],f[MAXN][18],id[MAXN],LN=0,z,st[MAXN];
     22 vector<int>e[MAXN],vt[MAXN],que;
     23 int init(int n)
     24 {
     25     z=0;
     26     memset(f,0,sizeof(f));
     27     for(int i=1; i<=n; i++)
     28         e[i].clear();
     29     d[0]=-1;
     30     LN=0;
     31 }
     32 void dfs(int v,int deep)
     33 {
     34     d[v]=deep;
     35     st[deep]=v;
     36     id[v]=++z;
     37     if((1<<LN)<deep)
     38         LN++;
     39     int i,j;
     40     for(i=1,j=0; i<=deep; i<<=1,j++)
     41     {
     42         f[v][j]=st[deep-i];
     43     }
     44     for(i=0; i<e[v].size(); i++)
     45     {
     46         if(e[v][i]!=f[v][0])
     47         {
     48             dfs(e[v][i],deep+1);
     49         }
     50     }
     51 }
     52 int lca(int x,int y)
     53 {
     54     if(d[x]<d[y])
     55         swap(x,y);
     56     int i;
     57     for(i=LN; i>=0; i--)
     58     {
     59         if(f[x][i]&&d[f[x][i]]>=d[y])
     60             x=f[x][i];
     61     }
     62     if(x==y)
     63         return x;
     64     for(i=LN; i>=0; i--)
     65     {
     66         if(f[x][i]>0&&f[x][i]!=f[y][i])
     67         {
     68             x=f[x][i];
     69             y=f[y][i];
     70         }
     71     }
     72     return f[x][0];
     73 }
     74 int cmp(const int &x,const int &y)
     75 {
     76     return id[x]<id[y];
     77 }
     78 int build(vector<int> &que)
     79 {
     80     int i,j,k,temp;
     81     sort(que.begin(),que.end(),cmp);
     82     stack<int>st;
     83     st.push(que[0]);
     84     vt[que[0]].clear();
     85     for(i=1; i<que.size(); i++)///每次有出栈的时候开始建边
     86     {
     87         k=lca(que[i],st.top());
     88         temp=0;
     89         while(!st.empty()&&lca(k,st.top())!=st.top())
     90         {
     91             if(temp)
     92                 vt[st.top()].push_back(temp);
     93             temp=st.top();
     94             st.pop();
     95         }
     96         if(st.empty()||st.top()!=k)
     97         {
     98             st.push(k);
     99             vt[k].clear();
    100         }
    101         if(temp)
    102             vt[st.top()].push_back(temp);
    103         st.push(que[i]);
    104         vt[que[i]].clear();
    105     }
    106     temp=0;
    107     while(!st.empty())
    108     {
    109         if(temp)
    110             vt[st.top()].push_back(temp);
    111         temp=st.top();
    112         st.pop();
    113     }
    114     return temp;
    115 }
    虚树

     习题:http://codeforces.com/problemset/problem/613/D

    这题需要树形DP基础,而且要讨论的情况有点多,所以其实有点不适合入门.....,因为很可能被树形DP卡住

  • 相关阅读:
    基于Spring+SpringMVC实现AOP日志记录功能service注入异常为null的解决办法
    关于SpringBoot项目打包没有把依赖的jar包一起打包的解决办法
    JavaFx项目打包成exe,并集成Jre,使Java项目在任意机器运行
    常用正则表达式
    SqlServer 2005及其以上版本能用的查询数据的行号
    js 中的倒计时功能
    数据库删除重复列
    【转】svn文件清除批处理工具
    JS获取当前页面名称
    sql 去除重复记录
  • 原文地址:https://www.cnblogs.com/qswg/p/8686216.html
Copyright © 2011-2022 走看看