虚树

这是一个坑...

 

给出一棵树.

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

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

我们可以用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的算法中倍增本来就是最慢的了囧......

原文地址:https://www.cnblogs.com/DragoonKiller/p/4555021.html