本文主要是介绍[BZOJ3572][Hnoi2014]世界树 虚树+DP,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
这玩意好难啊Orz
完全不理解那个模拟深搜到底是什么鬼
果然像我这样的人最好早点滚粗
要简历虚树 首先要选出虚树里面的点 那么关键点和关键点的LCA都要加入到虚树中来 那我们就深搜一遍 处理出每个节点的dfn值和儿子数
按照dfn值依次枚举每一个关键节点 这样可以把同一棵子树内的节点一起找到
用一个深度单调的栈来维护树中的节点 每次取出一个关键点 求出它和栈顶元素的lca
如果他们的lca的深度小于栈顶元素 lca的深度比栈中第二个元素大 即当前的lca卡在了两个树中的点之间 那么就由lca向当前的栈顶元素连边 无论是否连边 都直接把栈顶元素弹出
如果你当前这次没有连边 说明你没有卡在栈顶两个元素之间 那么他们在之前就已经练好一条边了 可以直接pop掉
当栈顶元素的深度已经比lca小 那么就由栈顶元素向lca连边
这样就完成建立虚树的操作
然后就是DP了
我们首先用两边for循环处理掉虚树上某条边 两个端点的元素会走到同一个端点 即x 走向fa[x] 或者 fa[x] 走向 x
然后我们枚举剩余的节点 它与父亲的边上的点不会都走向同一个点 那么我们求出他们距离的中点 中点以下的走向自己 中点以上的就走向父亲
果然还是好难啊QAQ
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#include<map>
#define SF scanf
#define PF printf
#define mp make_pair
#define fir first
#define sec second
#define bit(x) (1<<(x))
using namespace std;
typedef long long LL;
typedef pair <int, int> pii;
const int MAXN = 300000;
int n;
// Add edges
struct Node {int v, next;
} Edge[MAXN*2+10];
int adj[MAXN+10], ecnt;
void addedge(int u, int v) {Node &e = Edge[ecnt];e.v = v; e.next = adj[u]; adj[u] = ecnt++;
}
// DFS and build the tree
int dfn[MAXN+10], dcnt, f[MAXN+10][30], dep[MAXN+10], sz[MAXN+10];
void dfs(int u) {dfn[u] = ++dcnt;// prepare for LCAfor(int i = 1; bit(i) <= dep[u]; i++) f[u][i] = f[f[u][i-1]][i-1];for(int i = adj[u]; ~i; i = Edge[i].next) {int v = Edge[i].v;if(v == f[u][0]) continue;f[v][0] = u; dep[v] = dep[u]+1;dfs(v);sz[u] += sz[v];}sz[u]++;
}
// Go to another point
int Go(int x, int k) {int del = dep[x] - k;for(int i = 20; i >= 0; i--)if(del & bit(i))x = f[x][i];return x;
}
// find LCA
int LCA(int x, int y) {if(dep[x] < dep[y]) swap(x, y);int del = dep[x] - dep[y];for(int i = 20; i >= 0; i--)if(del & bit(i))x = f[x][i];if(x == y) return x;for(int i = 20; i >= 0; i--)if(f[x][i] != f[y][i])x = f[x][i], y = f[y][i];return f[x][0];
}
// solve the problem
int ask[MAXN+10], tree[MAXN+10], tmp[MAXN+10], tot, ans[MAXN+10];
int sta[MAXN+10], fa[MAXN+10], val[MAXN+10], in[MAXN+10];
pii near[MAXN+10];
bool cmp(int a, int b) { return dfn[a] < dfn[b]; }
void solve() {int m; SF("%d", &m);// Read the key pointsfor(int i = 1; i <= m; i++) {int x; SF("%d", &x);ask[i] = tree[i] = tmp[i] = x;near[x] = mp(0, x); ans[x] = 0;}sort(ask+1, ask+1+m, cmp);int top = 0;tot = m;// find the new tree pointsfor(int i = 1; i <= m; i++) {int cur = ask[i];if(!top) sta[++top] = cur, fa[cur] = 0;else {int lca = LCA(sta[top], cur);fa[cur] = lca;while(top && dep[sta[top]] > dep[lca]) {if(dep[sta[top-1]] <= dep[lca]) fa[sta[top]] = lca;top--;}if(sta[top] != lca) {fa[lca] = sta[top]; tree[++tot] = lca;sta[++top] = lca; near[lca] = mp(bit(30), 0);}sta[++top] = cur;}}sort(tree+1, tree+1+tot, cmp);// DPfor(int i = 1; i <= tot; i++) {int x = tree[i];val[x] = sz[x];if(i > 1) in[x] = dep[x] - dep[fa[x]];}for(int i = tot; i > 1; i--) {int x = tree[i], pre = fa[x];near[pre] = min(near[pre], mp(near[x].fir + in[x], near[x].sec));}for(int i = 2; i <= tot; i++) {int x = tree[i], pre = fa[x];near[x] = min(near[x], mp(near[pre].fir + in[x], near[pre].sec));}for(int i = 1; i <= tot; i++) {int x = tree[i], pre = fa[x], sum = sz[Go(x, dep[pre]+1)] - sz[x];if(pre == 0) ans[near[x].sec] += n-sz[x];else {val[pre] -= sum + sz[x];if(near[x].sec == near[pre].sec) ans[near[x].sec] += sum;else {int dis = (dep[x] - dep[pre] - near[x].fir + near[pre].fir) >> 1;if(dis + near[x].fir == near[pre].fir + dep[x] - dep[pre] - dis && near[pre].sec < near[x].sec) dis--;int p = Go(x, dep[x] - dis);ans[near[x].sec] += sz[p] - sz[x];ans[near[pre].sec] += sum + sz[x] - sz[p];}}}for(int i = 1; i <= tot; i++) ans[near[tree[i]].sec] += val[tree[i]];for(int i = 1; i <= m; i++) PF("%d ", ans[tmp[i]]);puts("");
}
// Main
int main() {memset(adj, -1, sizeof(adj));SF("%d", &n);for(int i = 1; i < n; i++) {int u, v; SF("%d%d", &u, &v);addedge(u, v); addedge(v, u);}dfs(1);int _T; SF("%d", &_T); while(_T--) solve();return 0;
}
这篇关于[BZOJ3572][Hnoi2014]世界树 虚树+DP的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!