zoukankan      html  css  js  c++  java
  • 虚树

    这是一个坑...

     

    给出一棵树.

    每次询问选择一些点,求一些东西.这些东西的特点是,许多未选择的点可以通过某种方式剔除而不影响最终结果.

    于是就有了建虚树这个技巧.....

    我们可以用log级别的时间求出点对间的lca....

    那么,对于每个询问我们根据原树的信息重新建树,这棵树中要尽量少地包含未选择节点. 这棵树就叫做虚树.

    接下来所说的"树"均指虚树,原来那棵树叫做"原树".

    构建过程如下:

    按照原树的dfs序号(记为dfn)递增顺序遍历选择的节点. 每次遍历节点都把这个节点插到树上.

    首先虚树一定要有一个根. 随便扯一个不会成为询问点的点作根.

    维护一个栈,它表示在我们已经(用之前的那些点)构建完毕的虚树上,以最后一个插入的点为端点的DFS链.

    设最后插入的点为p(就是栈顶的点),当前遍历到的点为x.我们想把x插入到我们已经构建的树上去.

    求出lca(p,x),记为lca.有两种情况:

      1.p和x分立在lca的两棵子树下.

      2.lca是p.

      (为什么lca不能是x?

       因为如果lca是x,说明dfn(lca)=dfn(x)<dfn(a),而我们是按照dfs序号遍历的,于是dfn(a)<dfn(x),矛盾.)

     对于第二种情况,直接在栈中插入节点x即可,不要连接任何边(后面会说为什么).

    对于第一种情况,要仔细分析.

    我们是按照dfs序号遍历的(因为很重要所以多说几遍......),有dfn(x)>dfn(p)>dfn(lca).

    这说明什么呢? 说明一件很重要的事:我们已经把lca所引领的子树中,p所在的子树全部遍历完了!

      简略的证明:如果没有遍历完,那么肯定有一个未加入的点h,满足dfn(h)<dfn(x),

            我们按照dfs序号递增顺序遍历的话,应该把h加进来了才能考虑x.

    这样,我们就直接构建lca引领的,p所在的那个子树. 我们在退栈的时候构建子树.

    p所在的子树如果还有其它部分,它一定在之前就构建好了(所有退栈的点都已经被正确地连入树中了),就剩那条链.

    如何正确地把p到lca那部分连进去呢?

    设栈顶的节点为p,栈顶第二个节点为q.

    重复以下操作:

      如果dfn(q)>dfn(lca),可以直接连边q->p,然后退一次栈.

      如果dfn(q)=dfn(lca),说明q=lca,直接连边lca->p,此时子树已经构建完毕.

      如果dfn(q)<dfn(lca),说明lca被p与q夹在中间,此时连边lca->q,退一次栈,再把lca压入栈.此时子树构建完毕.

        如果不理解这样操作的缘由可以画画图.....

    最后,为了维护dfs链,要把x压入栈. 整个过程就是这样.....

    AC BZOJ 2286

    跑得好慢!

      1 #include <cstdio>
      2 #include <fstream>
      3 #include <iostream>
      4  
      5 #include <cstdlib>
      6 #include <cstring>
      7 #include <algorithm>
      8 #include <cmath>
      9  
     10 #include <queue>
     11 #include <vector>
     12 #include <map>
     13 #include <set>
     14 #include <stack>
     15 #include <list>
     16  
     17 typedef unsigned int uint;
     18 typedef long long int ll;
     19 typedef unsigned long long int ull;
     20 typedef double db;
     21  
     22 using namespace std;
     23  
     24 inline int getint()
     25 {
     26     int res=0;
     27     char c=getchar();
     28     bool mi=false;
     29     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
     30     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
     31     return mi ? -res : res;
     32 }
     33 inline ll getll()
     34 {
     35     ll res=0;
     36     char c=getchar();
     37     bool mi=false;
     38     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
     39     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
     40     return mi ? -res : res;
     41 }
     42 
     43 //==============================================================================
     44 //==============================================================================
     45 //==============================================================================
     46 //==============================================================================
     47 
     48 const int INF=(1<<30)-1;
     49 
     50 struct edge
     51 { int in; int v; edge*nxt; };
     52 edge pool[2][1005000];
     53 edge*et[]={pool[0],pool[1]};
     54 edge*eds[2][255000];
     55 void addedge(int a,int b,int v,int k)
     56 {et[k]->in=b; et[k]->v=v; et[k]->nxt=eds[k][a]; eds[k][a]=et[k]++; }
     57 #define FOREACH_EDGE(i,j,k) for(edge*i=eds[k][j];i;i=i->nxt)
     58 //[0] is for original graph, [1] is for query.
     59 
     60 int n,m;
     61 
     62 int f[19][255000];
     63 int mi[19][255000];
     64 int flim=18;
     65 int dep[255000];
     66 int dcnt=0;
     67 int loc[255000];
     68 void Build(int x)
     69 {
     70     loc[x]=dcnt;
     71     dcnt++;
     72     
     73     FOREACH_EDGE(e,x,0)
     74     if(e->in!=f[0][x])
     75     {
     76         f[0][e->in]=x;
     77         mi[0][e->in]=e->v;
     78         dep[e->in]=dep[x]+1;
     79         Build(e->in);
     80     }
     81 }
     82 
     83 void Gen()
     84 {
     85     for(int d=1;d<=flim;d++)
     86     for(int i=0;i<n;i++)
     87     {
     88         f[d][i]=f[d-1][f[d-1][i]];
     89         mi[d][i]=min(mi[d-1][i],mi[d-1][f[d-1][i]]);
     90     }
     91 }
     92 
     93 int getlca(int a,int b)
     94 {
     95     if(dep[a]<dep[b]) swap(a,b);
     96     
     97     int d=dep[a]-dep[b];
     98     for(int i=flim;i>=0;i--)
     99     if((d>>i)&1) a=f[i][a];
    100     
    101     if(a==b) return a;
    102     
    103     for(int i=flim;i>=0;i--)
    104     if(f[i][a]!=f[i][b]) a=f[i][a],b=f[i][b];
    105     
    106     return f[0][a];
    107 }
    108 
    109 int getmin(int a,int fa)
    110 { 
    111     int res=INF;
    112     int d=dep[a]-dep[fa];
    113     for(int i=flim;i>=0;i--)
    114     if((d>>i)&1)
    115     res=min(res,mi[i][a]),a=f[i][a];
    116     return res;
    117 }
    118 
    119 int a[505000];
    120 inline bool cmp(const int&a,const int&b)
    121 { return loc[a]<loc[b]; }
    122 bool need[505000];
    123 
    124 ll DP(int x)
    125 {
    126     ll res=0;
    127     FOREACH_EDGE(e,x,1)
    128     res+=( need[e->in] ? e->v : min((ll)e->v,DP(e->in)) );
    129     return res;
    130 }
    131 
    132 void Clear(int x)
    133 {
    134     FOREACH_EDGE(e,x,1) Clear(e->in);
    135     eds[1][x]=NULL;
    136 }
    137 
    138 
    139 int s[505000],st;
    140 int main()
    141 {
    142     n=getint();
    143     
    144     for(int i=1;i<n;i++)
    145     {
    146         int a=getint()-1;
    147         int b=getint()-1;
    148         int c=getint();
    149         addedge(a,b,c,0);
    150         addedge(b,a,c,0);
    151     }
    152     
    153     f[0][0]=0; mi[0][0]=INF; dep[0]=0;
    154     Build(0);
    155     
    156     Gen();
    157     
    158     int T=getint();
    159     while(T--)
    160     {
    161         m=getint();
    162         for(int i=0;i<m;i++)
    163         a[i]=getint()-1;
    164         
    165         sort(a,a+m,cmp);
    166         
    167         //Init Tree
    168         et[1]=pool[1]; //delete all edges.
    169         for(int i=0;i<m;i++) need[a[i]]=true;
    170         
    171         //Build new Tree
    172         st=0;
    173         s[st++]=0; //root will be always here.
    174         for(int i=0;i<m;i++)
    175         {
    176             int x=a[i]; 
    177             int lca=getlca(x,s[st-1]);
    178             
    179             while(st>1 && loc[lca]<loc[s[st-2]])
    180             --st,addedge(s[st-1],s[st],getmin(s[st],s[st-1]),1);
    181             
    182             if(s[st-1]!=lca)
    183             {
    184                 --st,addedge(lca,s[st],getmin(s[st],lca),1);
    185                 if(s[st-1]!=lca) s[st++]=lca;
    186             }
    187             
    188             if(x!=s[st-1]) s[st++]=x;
    189         }
    190         
    191         while(st>1)
    192         --st,addedge(s[st-1],s[st],getmin(s[st],s[st-1]),1);
    193         
    194         //Calculate
    195         printf("%lld
    ",DP(0));
    196         
    197         //Clear the new tree.
    198         for(int i=0;i<m;i++) need[a[i]]=false;
    199         Clear(0);
    200     }
    201     
    202     return 0;
    203 }
    View Code

    以后写倍增LCA就按照这个写....否则更加慢........在求LCA的算法中倍增本来就是最慢的了囧......

  • 相关阅读:
    剑指offer63:数据流中的中位数
    剑指offer62:二叉搜索树的第k个结点,二叉搜索树【左边的元素小于根,右边的元素大于根】
    剑指offer61:序列化二叉树
    关于手机拍摄的图片在处理过程中出现问题的解决方法
    一道逻辑思考题
    鼠标右键无反应解决方法
    六大设计原则
    开源镜像网站
    获取当前文件夹下的文件名称
    wget使用方法
  • 原文地址:https://www.cnblogs.com/DragoonKiller/p/4555021.html
Copyright © 2011-2022 走看看