虚树
这是一个坑...
给出一棵树.
每次询问选择一些点,求一些东西.这些东西的特点是,许多未选择的点可以通过某种方式剔除而不影响最终结果.
于是就有了建虚树这个技巧.....
我们可以用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\n",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 }
以后写倍增LCA就按照这个写....否则更加慢........在求LCA的算法中倍增本来就是最慢的了囧......