[BZOJ3572][Hnoi2014]世界树 && 虚树+DP

来源:互联网 发布:mac 设置homekit 编辑:程序博客网 时间:2024/05/18 14:22

这玩意好难啊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 edgesstruct 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 treeint dfn[MAXN+10], dcnt, f[MAXN+10][30], dep[MAXN+10], sz[MAXN+10];void dfs(int u) {    dfn[u] = ++dcnt;    // prepare for LCA    for(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 pointint 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 LCAint 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 problemint 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 points    for(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 points    for(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);// DP    for(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("");}// Mainint 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;}


0 0
原创粉丝点击