题面

[HNOI2014]世界树

题解

从数据范围很容易看出是个虚树DP(可惜看出来了也还是不会做)

虚树大家应该都会, 不会的话自己去搜吧, 我懒得讲了, 我们在这里只需要考虑如何DP即可

首先我们需要求出每个点被哪个点所控制, 设\(u\)点被\(bl[u]\)所控制, 两遍DFS即可, 考虑儿子对父亲的影响和父亲对儿子的影响

代码细节相信不要我说, 能做这道题的总不可能不会DFS吧

还是贴一下自己看吧

void dfs1(int u, int fa)
{
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to; if(v == fa) continue;
dfs1(v, u); int dv = dep[bl[v]] - dep[u], du = bl[u] ? dep[bl[u]] - dep[u] : 0x3f3f3f3f;
if(dv < du || (dv == du && bl[v] < bl[u])) bl[u] = bl[v];
}
}
void dfs2(int u, int fa)
{
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to; if(v == fa) continue;
int dv = dis(bl[v], v), du = dis(bl[u], v);
if(du < dv || (du == dv && bl[u] < bl[v])) bl[v] = bl[u];
dfs2(v, u);
}
}
//分别在递归前和递归后处理一下就完事了

然后考虑如何计算答案, 对虚树上每一条边讨论

①: 边的两端被同一个节点所控制, 加上这两个点不在虚树中的儿子的sz即可

②: 边的两端被不同点控制, 我们需要找出一个分界点, 满足此分界点归下面那个点的\(bl[]\)所控制, 此分界点的父亲被上面那个点的\(bl[]\)所控制, 用个数据结构维护一下或者倍增跳一下就可以了

至于每个点不在虚树中的儿子的sz, 拿当前点的sz减去所有他在虚树中的儿子的sz即可

具体实现细节参见代码(参考了一下题解的思路嘿嘿嘿)

代码实现

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define N 300005
using namespace std; int n, m, Q, head[N], sz[N], son[N], dep[N], dfn[N], f[N][21], top[N], cnt, tp, a[N], b[N], stk[N], bl[N], con[N], ans[N], l[N];
struct edge { int from, to, next; } e[N << 1]; inline int read()
{
int x = 0, w = 1;
char c = getchar();
while(c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * w;
} inline void add(int u, int v) { e[++cnt] = (edge) { u, v, head[u] }; head[u] = cnt; } void dfs_sz(int u, int fa)
{
sz[u] = 1; dep[u] = dep[fa] + 1; f[u][0] = fa;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to; if(v == fa) continue;
dfs_sz(v, u); sz[u] += sz[v]; if(sz[son[u]] < sz[v]) son[u] = v;
}
} void dfs_top(int x, int y)
{
dfn[x] = ++cnt; top[x] = y;
if(!son[x]) return; dfs_top(son[x], y);
for(int i = head[x]; i; i = e[i].next) if(e[i].to != f[x][0] && e[i].to != son[x]) dfs_top(e[i].to, e[i].to);
} int LCA(int x, int y)
{
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) swap(x, y);
x = f[top[x]][0];
}
return dep[x] < dep[y] ? x : y;
} bool cmp(int x, int y) { return dfn[x] < dfn[y]; } void dfs1(int u, int fa)
{
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to; if(v == fa) continue;
dfs1(v, u); int dv = dep[bl[v]] - dep[u], du = bl[u] ? dep[bl[u]] - dep[u] : 0x3f3f3f3f;
if(dv < du || (dv == du && bl[v] < bl[u])) bl[u] = bl[v];
}
} int dis(int x, int y) { return dep[x] + dep[y] - 2 * dep[LCA(x, y)]; } void dfs2(int u, int fa)
{
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to; if(v == fa) continue;
int dv = dis(bl[v], v), du = dis(bl[u], v);
if(du < dv || (du == dv && bl[u] < bl[v])) bl[v] = bl[u];
dfs2(v, u);
}
} void dp(int u)
{
for(int s, mid, nt, dv, du, i = head[u]; i; i = e[i].next)
{
int v = e[i].to; dp(v); s = mid = v;
for(int j = l[dep[v]]; j >= 0; j--) if(dep[f[s][j]] > dep[u]) s = f[s][j];
con[u] -= sz[s];
if(bl[u] == bl[v]) { ans[bl[u]] += sz[s] - sz[v]; continue; }
for(int j = l[dep[v]]; j >= 0; j--)
{
nt = f[mid][j]; if(dep[nt] <= dep[u]) continue;
dv = dis(bl[v], nt), du = dis(bl[u], nt);
if(dv < du || (dv == du && bl[v] < bl[u])) mid = nt;
}
ans[bl[u]] += sz[s] - sz[mid];
ans[bl[v]] += sz[mid] - sz[v];
}
ans[bl[u]] += con[u];
} void query()
{
m = read(); cnt = tp = 0;
for(int i = 1; i <= m; i++) bl[a[++cnt] = b[i] = read()] = b[i];
sort(a + 1, a + cnt + 1, cmp);
for(int i = 1; i < m; i++) a[++cnt] = LCA(a[i], a[i + 1]);
a[++cnt] = 1; sort(a + 1, a + cnt + 1, cmp);
int len = unique(a + 1, a + cnt + 1) - a - 1;
cnt = 0; for(int i = 1; i <= len; i++) head[a[i]] = 0, con[a[i]] = sz[a[i]];
for(int i = 1; i <= len; i++)
{
while(tp && dfn[a[i]] >= dfn[stk[tp]] + sz[stk[tp]]) tp--;
if(tp) add(stk[tp], a[i]); stk[++tp] = a[i];
}
dfs1(1, 0); dfs2(1, 0); dp(1);
for(int i = 1; i <= m; i++) printf("%d%c", ans[b[i]], i == m ? '\n' : ' ');
for(int i = 1; i <= len; i++) bl[a[i]] = ans[a[i]] = con[a[i]] = 0;
} int main()
{
n = read(); for(int i = 2; i <= n; i++) l[i] = l[i >> 1] + 1;
for(int i = 1; i < n; i++) { int u = read(), v = read(); add(u, v); add(v, u); }
cnt = 0; dfs_sz(1, 0); dfs_top(1, 0);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= 20; j++)
f[i][j] = f[f[i][j - 1]][j - 1];
Q = read(); while(Q--) query();
return 0;
}

[题解] [HNOI2014] 世界树的更多相关文章

  1. [BZOJ3572][Hnoi2014]世界树

    [BZOJ3572][Hnoi2014]世界树 试题描述 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条 ...

  2. 【BZOJ3572】[Hnoi2014]世界树 虚树

    [BZOJ3572][Hnoi2014]世界树 Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森 ...

  3. bzoj3572[Hnoi2014] 世界树 虚树+dp+倍增

    [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1921  Solved: 1019[Submit][Status][Dis ...

  4. BZOJ 3572: [Hnoi2014]世界树

    BZOJ 3572: [Hnoi2014]世界树 标签(空格分隔): OI-BZOJ OI-虚数 OI-树形dp OI-倍增 Time Limit: 20 Sec Memory Limit: 512 ...

  5. bzoj 3572: [Hnoi2014]世界树 虚树 && AC500

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 520  Solved: 300[Submit][Status] ...

  6. bzoj 3572 [Hnoi2014]世界树(虚树+DP)

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 645  Solved: 362[Submit][Status] ...

  7. <虚树+树型DP> HNOI2014世界树

    <虚树+树型DP> HNOI2014世界树 #include <iostream> #include <cstdio> #include <cstring&g ...

  8. BZOJ3572:[HNOI2014]世界树——题解

    +++++++++++++++++++++++++++++++++++++++++++ +本文作者:luyouqi233. + +欢迎访问我的博客:http://www.cnblogs.com/luy ...

  9. 【题解】HNOI2014世界树

    脑子不清醒的时候千万别写题.写题写不下去了千万别死扛,重构才是你唯一的出路QAQ 昨天很想快点写道题,思路没有很清晰的时候就写了,结果……今天一怒之下决定重整思路重构代码,其实不过是半个小时的事情…… ...

随机推荐

  1. 手把手教你搭建FastDFS集群(上)

    手把手教你搭建FastDFS集群(上) 本文链接:https://blog.csdn.net/u012453843/article/details/68957209        FastDFS是一个 ...

  2. vue的使用与安装 npm -v报错

    1.先将node从官方文档下载下来,然后进行安装. 安装成功后,在dos命令中node -v.npm -v来测试,如果成功就可以安装cnpm(国内淘宝镜像比较快).这里我遇到一个bug,npm -v压 ...

  3. SQL Tuning 基础概述10

    在<SQL Tuning 基础概述05 - Oracle 索引类型及介绍>的1.5小节,提到了几种"索引的常见执行计划": INDEX FULL SCAN:索引的全扫描 ...

  4. 第二章·Elasticsearch内部分片及分片处理机制介绍

    一.副本分片介绍 什么是副本分片? 副本分片的主要目的就是为了故障转移,如果持有主分片的节点挂掉了,一个副本分片就会晋升为主分片的角色. 在索引写入时,副本分片做着与主分片相同的工作.新文档首先被索引 ...

  5. Summer training #5

    B:分析序列 构造树(优先队列) #include <bits/stdc++.h> #include <cstring> #include <iostream> # ...

  6. Python main()函数解析

    __main__ — Top-level script environment '__main__'是顶级代码执行的作用域的名字. 当一个模块从标准input, 一个脚本文件,或一个交互命令read读 ...

  7. P4294 [WC2008]游览计划 (斯坦纳树)

    题目链接 差不多是斯坦纳树裸题,不过边权化成了点权,这样在合并两棵子树时需要去掉根结点的权值,防止重复. 题目还要求输出解,只要在转移时记录下路径,然后dfs一遍就好了. #include<bi ...

  8. shell的正则表达式

    正则表达式处理文件的内容,shell处理文件本身 grep *匹配0到n个 .(点儿)能匹配任意字符----8.8.8.8用于测试外网是否通畅 egrep

  9. BZOJ 1107: [POI2007]驾驶考试egz / Luogu P3463 [POI2007]EGZ-Driving Exam (树状数组 LIS)

    能从iii走到所有跑道 相当于 能从iii走到111和nnn. 边反向后就相当于 能从111和nnn走到iii. 为了方便叙述,把111~nnn叫做x坐标,111~(m+1)(m+1)(m+1)叫做y ...

  10. BZOJ 3881[COCI2015]Divljak (AC自动机+dfs序+lca+BIT)

    显然是用AC自动机 先构建好AC自动机,当B中插入新的串时就在trie上跑,对于当前点,首先这个点所代表的串一定出现过,然后这个点指向的fail也一定出现过.那么我们把每个点fail当作父亲,建一棵f ...