虽然这个名字听起来非常玄妙但是实际上是一种非常朴素的思想啊。。。
有的时候会遇到一类问题: 有一棵树, 每次选出来mi个点, 询问关于这mi个点的一些信息。 保证Σmi 不是很大。
这时候一些笨重的方法诸如树剖,LCT就会不太好用。
一些时候可以使用点分治, 在传统点分治的基础上如果可以做到一棵子树中没有询问点就不去管它了, 那么最终访问的点是 mlogm 级别的。
虚树是一种更加直接的想法: 既然询问点的个数不多, 我们不妨把它们提出来, 单独建成一棵树。
为了满足新树和原树的形态相同,新树中显然还要包含原来那些询问点的分叉节点, 即每两个询问点之间的LCA。(这听起来似乎是 m^2 个点?? 但是稍微想一下就可以发现其实这些点的个数是严格<n的。。(一棵不包含只有一个儿子的节点的树 的叶子节点个数显然大于非叶子节点个数))
下面考虑如何把这棵树建出来。
假设已经建完了前i-1个点, 现在考虑把第i个点插入进去。肯定要沿着当前建出来的树向第i个点走啊走, 走到不能再走(当前树中没有这条路)的时候拐弯, 自己接着走。(这里有种后缀自动机的即视感)于是新建出两个点:i号点自己和那个拐弯的点。那个拐弯的点在哪里呢?很显然就是当前树中所有点和i号点的LCA中深度最深的那一个。而和i的LCA最深的那个点要么是dfn<dfn[i]中最大的那个, 要么就是dfn>dfn[i]中最小的那个,这个随便拿一个set维护一下就好啦。当然,大部分题目在建树的这部分中是兹瓷离线的, 于是按dfs需排个序然后扫一遍就可以啦。
题目
bzoj 2286: [Sdoi2011消耗战
用虚树做非常简单,没什么好说的啦。
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <algorithm> 5 #include <cstring> 6 #define MAXN 500005 7 #define ll long long 8 using namespace std; 9 int n, Q, head[MAXN], ee, cnt; 10 int dep[MAXN], skip[MAXN][18], dfn[MAXN], mn[MAXN][18]; 11 struct Edge{int to, next, c;}edge[MAXN*2]; 12 inline void addedge(int x, int y, int c){edge[++ ee].to = y; edge[ee].next = head[x]; edge[ee].c = c; head[x] = ee;} 13 void dfs(int x, int fatt, int cc){ 14 dep[x] = dep[fatt] + 1; 15 dfn[x] = ++ cnt; 16 skip[x][0] = fatt; mn[x][0] = cc; 17 for(int i = 1; i <= 17; i ++) skip[x][i] = skip[skip[x][i-1]][i-1]; 18 for(int i = 1; i <= 17; i ++) mn[x][i] = min(mn[x][i-1], mn[skip[x][i-1]][i-1]); 19 for(int i = head[x]; i != -1; i = edge[i].next) if(edge[i].to != fatt) dfs(edge[i].to, x, edge[i].c); 20 } 21 int m, h[MAXN], st[MAXN], top, T[MAXN], tt, fat[MAXN], ha[MAXN], key; 22 ll ans[MAXN]; 23 bool cmp(int a, int b){return dfn[a] < dfn[b];} 24 int LCA(int x, int y){ 25 if(dep[x] < dep[y]) swap(x, y); 26 for(int i = 17; i >= 0; i --) if(dep[skip[x][i]] >= dep[y]) x = skip[x][i]; 27 if(x == y) return x; 28 for(int i = 17; i >= 0; i --) if(skip[x][i] != skip[y][i]) x = skip[x][i], y = skip[y][i]; 29 return skip[x][0]; 30 } 31 int query(int x, int dd){ 32 int ret = 1<<30; 33 for(int i = 17; i >= 0; i --) if(dep[skip[x][i]] >= dd) ret = min(ret, mn[x][i]), x = skip[x][i]; 34 return ret; 35 } 36 inline void solve(){ 37 scanf("%d", &m); 38 top = tt = 0; 39 T[++ tt] = 1; ans[1] = 0; st[++ top] = 1; 40 for(int i = 1; i <= m; i ++){ 41 scanf("%d", &h[i]); 42 T[++ tt] = h[i]; ans[h[i]] = 0; 43 ha[h[i]] = key; 44 } 45 sort(h + 1, h + m + 1, cmp); 46 for(int i = 1; i <= m; i ++){ 47 int lca = LCA(st[top], h[i]), la = top; 48 while(dep[st[top]] > dep[lca]) top --; 49 if(la != top) fat[st[top+1]] = lca; 50 if(lca != st[top]){ 51 fat[lca] = st[top]; 52 st[++ top] = lca; 53 T[++ tt] = lca; ans[lca] = 0; 54 } 55 fat[h[i]] = lca; 56 st[++ top] = h[i]; 57 } 58 sort(T + 1, T + tt + 1, cmp); 59 for(int i = tt; i > 1 ; i --){ 60 ll la = query(T[i], dep[fat[T[i]]]); 61 if(ha[T[i]] != key) la = min(la, ans[T[i]]); 62 ans[fat[T[i]]] += la; 63 } 64 printf("%lld ", ans[1]); 65 } 66 int main(){ 67 scanf("%d", &n); 68 memset(head, -1, sizeof(head)); 69 for(int i = 1; i < n; i ++){ 70 int x, y, c; scanf("%d%d%d", &x, &y, &c); 71 addedge(x, y, c); addedge(y, x, c); 72 } 73 dfs(1, 0, 0); 74 scanf("%d", &Q); 75 for(int i = 1; i <= Q; i ++) key = i, solve(); 76 return 0; 77 }
bzoj 3572: [Hnoi2014]世界树
可以点分治。细节很多QAQ。
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <algorithm> 5 #include <cstring> 6 #include <map> 7 #define mp make_pair 8 #define MAXN 300005 9 using namespace std; 10 int n, ee, Q, dfn[MAXN], fat[MAXN], sz[MAXN], dep[MAXN], cnt, m, h[MAXN], skip[MAXN][19], head[MAXN]; 11 int xv[MAXN*2], tot, st[MAXN*2], top, mem[MAXN], ans[MAXN], dis[MAXN]; 12 struct Edge{ 13 int to, next; 14 }edge[MAXN*2]; 15 inline void addedge(int x, int y){ 16 edge[++ ee].to = y; edge[ee].next = head[x]; head[x] = ee; 17 } 18 void dfs(int x, int fatt){ 19 sz[x] = 1; dep[x] = dep[fatt] + 1; dfn[x] = ++ cnt; 20 skip[x][0] = fatt; 21 for(int i = 1; i <= 18; i ++) skip[x][i] = skip[skip[x][i-1]][i-1]; 22 for(int i = head[x]; i != -1; i = edge[i].next) if(edge[i].to != fatt) dfs(edge[i].to, x), sz[x] += sz[edge[i].to]; 23 } 24 int LCA(int x, int y){ 25 if(dep[x] < dep[y]) swap(x, y); 26 for(int i = 18; i >= 0; i --) if(dep[skip[x][i]] >= dep[y]) x = skip[x][i]; 27 if(x == y) return x; 28 for(int i = 18; i >= 0; i --) if(skip[x][i] != skip[y][i]) x = skip[x][i], y = skip[y][i]; 29 return skip[x][0]; 30 } 31 int find(int x, int p){ 32 for(int i = 18; i >= 0; i --) if(dep[skip[x][i]] >= p) x = skip[x][i]; 33 return x; 34 } 35 bool cmp(int a, int b){return dfn[a] < dfn[b];} 36 std::pair<int,int> f[MAXN]; 37 void solve(){ 38 scanf("%d", &m); 39 tot = top = 0; 40 for(int i = 1; i <= m; i ++) { 41 scanf("%d", &h[i]); 42 mem[i] = h[i]; 43 ans[h[i]] = 0; 44 xv[++ tot] = h[i]; 45 f[h[i]] = mp(0, h[i]); 46 } 47 sort(h + 1, h + m + 1, cmp); 48 for(int i = 1; i <= m; i ++) 49 if(!top) fat[st[++ top] = h[i]] = 0; 50 else{ 51 int lca = LCA(h[i], st[top]), la = top; 52 while(dep[st[top]] > dep[lca]) top --; 53 if(la != top)fat[st[top+1]] = lca; 54 if(lca != st[top]){ 55 xv[++ tot] = lca; 56 f[lca] = mp(1<<30, 0); 57 fat[lca] = st[top]; 58 st[++ top] = lca; 59 } 60 fat[h[i]] = st[top]; 61 st[++ top] = h[i]; 62 } 63 sort(xv + 1, xv + tot + 1, cmp); 64 for(int i = 2; i <= tot; i ++){ 65 int p = xv[i]; 66 dis[p] = dep[p] - dep[fat[p]]; 67 } 68 for(int i = tot; i > 1 ; i --){ 69 int p = xv[i], fa = fat[p]; 70 f[fa] = min(f[fa], mp(f[p].first + dis[p], f[p].second)); 71 } 72 for(int i = 1; i <= tot; i ++){ 73 int p = xv[i], fa = fat[p]; 74 if(i > 1) 75 f[p] = min(f[p], mp(f[fa].first + dis[p], f[fa].second)); 76 ans[f[p].second] += sz[p]; 77 } 78 ans[f[xv[1]].second] += n - sz[xv[1]]; 79 for(int i = 2; i <= tot; i ++){ 80 int p = xv[i], fa = fat[p]; 81 int x = find(p, dep[fa] + 1), sum = sz[x] - sz[p]; 82 ans[f[fa].second] -= sz[x]; 83 if(f[fa].second == f[p].second) ans[f[p].second] += sum; 84 else{ 85 int mid = dep[p] - ((f[fa].first + f[p].first + dis[p]) / 2 - f[p].first); 86 if((f[fa].first + f[p].first + dis[p]) % 2 == 0 && f[p].second > f[fa].second) mid ++; 87 int y = sz[find(p, mid)] - sz[p]; 88 ans[f[p].second] += y; 89 ans[f[fa].second] += sum-y; 90 } 91 } 92 for(int i = 1; i <= m; i ++) printf("%d ", ans[mem[i]]); cout << endl; 93 } 94 int main(){ 95 memset(head, -1, sizeof(head)); 96 scanf("%d", &n); 97 for(int i = 1; i < n; i ++){ 98 int x, y; scanf("%d%d", &x, &y); addedge(x, y); addedge(y, x); 99 } 100 dfs(1, 0); 101 scanf("%d", &Q); 102 for(int i = 1; i <= Q; i ++) solve(); 103 return 0; 104 }