[BZOJ3572][Hnoi2014]世界树
[BZOJ3572][Hnoi2014]世界树
试题描述
世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种族的编号分别从1到n,分别生活在编号为1到n的聚居地上,种族的编号与其聚居地的编号相同。有的聚居地之间有双向的道路相连,道路的长度为1。保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环。定义两个聚居地之间的距离为连接他们的道路的长度;例如,若聚居地a和b之间有道路,b和c之间有道路,因为每条道路长度为1而且又不可能出现环,所卧a与c之间的距离为2。
出于对公平的考虑,第i年,世界树的国王需要授权m[i]个种族的聚居地为临时议事处。对于某个种族x(x为种族的编号),如果距离该种族最近的临时议事处为y(y为议事处所在聚居地的编号),则种族x将接受y议事处的管辖(如果有多个临时议事处到该聚居地的距离一样,则y为其中编号最小的临时议事处)。
现在国王想知道,在q年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。 现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。
输入
第一行为一个正整数n,表示世界树中种族的个数。
接下来n-l行,每行两个正整数x,y,表示x聚居地与y聚居地之间有一条长度为1的双
向道路。接下来一行为一个正整数q,表示国王询问的年数。
接下来q块,每块两行:
第i块的第一行为1个正整数m[i],表示第i年授权的临时议事处的个数。
第i块的第二行为m[i]个正整数h[l]、h[2]、…、h[m[i]],表示被授权为临时议事处的聚居地编号(保证互不相同)。
输出
输出包含q行,第i行为m[i]个整数,该行的第j(j=1,2…,,m[i])个数表示第i年被授权的聚居地h[j]的临时议事处管理的种族个数。
输入示例
输出示例
数据规模及约定
N<=300000, q<=300000,m[1]+m[2]+…+m[q]<=300000
题解
建出虚树后 dp 求出距离虚树上每个点最近的议事点在哪以及到它的距离。
然后考虑虚树上每条边对两个端点所对应的议事点的答案的贡献,不妨设两个端点分别为 a 和 b,边长为 d,a 到它最近的议事点距离为 f[a],b 到它最近的议事点距离为 f[b],那么需要求这么一个 x 使得 f[a] + x = f[b] + (d - x),解得 x = [(f[b] - f[a] + d) / 2](向下取整)。这个 x 是干什么的呢?就是从 b 在原树中向上走 x 单位的长度所经过的点的议事点都是 b 所对应的议事点(因为这些点离 b 更近),在往上的点就该去 a 了,注意当 (f[b] - f[a] + d) 为偶数时,会有一个正中间的点,即到 a 和到 b 的距离相等,这时记得特判一下。我们把 b 向上 x 个单位的点设为 mid。那么这条边对于 b 的贡献为 siz[mid] - siz[b],对于 a 的贡献为 siz[son] - siz[mid],其中 son 是 a, b 路径上 a 的第一个儿子,siz[u] 表示原树中以 u 为根的子树的大小。注意上面的都是不包含 a, b 两个点的,还需要把没被统计的点加上,这个东西不难处理,读者不妨想一想。
还有这题输出格式是行末带空格的。。。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std; int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
} #define maxn 300010
#define maxm 600010
#define maxlog 20
#define oo 2147483647
int n, m, head[maxn], next[maxm], to[maxm]; void AddEdge(int a, int b) {
to[++m] = b; next[m] = head[a]; head[a] = m;
swap(a, b);
to[++m] = b; next[m] = head[a]; head[a] = m;
return ;
} int ord[maxn], clo, dep[maxn], fa[maxlog][maxn], siz[maxn];
void build(int u) {
ord[u] = ++clo; siz[u] = 1;
for(int i = 1; i < maxlog; i++) fa[i][u] = fa[i-1][fa[i-1][u]];
for(int e = head[u]; e; e = next[e]) if(to[e] != fa[0][u]) {
dep[to[e]] = dep[u] + 1;
fa[0][to[e]] = u;
build(to[e]);
siz[u] += siz[to[e]];
}
return ;
}
int lca(int a, int b) {
if(dep[a] < dep[b]) swap(a, b);
for(int i = maxlog - 1; i >= 0; i--) if(dep[a] - (1 << i) >= dep[b]) a = fa[i][a];
for(int i = maxlog - 1; i >= 0; i--) if(fa[i][a] != fa[i][b]) a = fa[i][a], b = fa[i][b];
return a == b ? a : fa[0][b];
} bool cmp(int a, int b) { return ord[a] < ord[b]; }
int cpi, Ps[maxn], psi[maxn], cp, ps[maxn], vis[maxn];
bool flg[maxn];
int M, Head[maxn], Next[maxm], To[maxm], Dist[maxm];
void Add2(int a, int b, int c) {
// printf("%d -- %d : %d\n", a, b, c);
To[++M] = b; Dist[M] = c; Next[M] = Head[a]; Head[a] = M;
swap(a, b);
To[++M] = b; Dist[M] = c; Next[M] = Head[a]; Head[a] = M;
return ;
}
int f[maxn], id[maxn], ans[maxn];
void dp1(int u, int pa) {
// printf("u: %d\n", u);
f[u] = id[u] = oo;
if(flg[u]) f[u] = 0, id[u] = u; flg[u] = 0;
for(int e = Head[u]; e; e = Next[e]) if(To[e] != pa) {
dp1(To[e], u);
int tmp = f[To[e]] + Dist[e];
if(tmp < f[u]) f[u] = tmp, id[u] = id[To[e]];
else if(tmp == f[u] && id[To[e]] < id[u]) id[u] = id[To[e]];
}
return ;
}
void dp2(int u, int pa) {
for(int e = Head[u]; e; e = Next[e]) if(To[e] != pa) {
int tmp = f[u] + Dist[e];
if(tmp < f[To[e]]) f[To[e]] = tmp, id[To[e]] = id[u];
else if(tmp == f[To[e]] && id[u] < id[To[e]]) id[To[e]] = id[u];
dp2(To[e], u);
}
// printf("f[%d] = %d, id[%d] = %d\n", u, f[u], u, id[u]);
return ;
}
int jump(int u, int d) {
for(int i = maxlog - 1; i >= 0; i--) if(d >= (1 << i))
d -= (1 << i), u = fa[i][u];
return u;
}
void process(int u, int pa) {
int S = siz[u];
for(int e = Head[u]; e; e = Next[e]) if(To[e] != pa) {
int tmp = f[u] - f[To[e]] + Dist[e] >> 1;
if(f[u] - f[To[e]] + Dist[e] & 1) ; else tmp -= id[u] < id[To[e]];
int mid = jump(To[e], tmp), son = jump(To[e], Dist[e] - 1);
// printf("processing: (%d %d) mid(%d) son(%d)\n", u, To[e], mid, son);
ans[id[u]] += siz[son] - siz[mid];
ans[id[To[e]]] += siz[mid] - siz[To[e]];
S -= siz[son];
process(To[e], u);
}
ans[id[u]] += S;
Head[u] = 0;
return ;
} int main() {
n = read();
for(int i = 1; i < n; i++) {
int a = read(), b = read();
AddEdge(a, b);
}
build(1);
// for(int i = 1; i <= n; i++) printf("%d%c", ord[i], i < n ? ' ' : '\n');
int q = read();
while(q--) {
cpi = read(); cp = 0;
for(int i = 1; i <= cpi; i++) {
Ps[i] = psi[i] = read();
ps[++cp] = psi[i];
flg[ps[cp]] = 1;
vis[ps[cp]] = q + 1;
}
sort(psi + 1, psi + cpi + 1, cmp);
for(int i = 1; i < cpi; i++) {
int c = lca(psi[i], psi[i+1]);
if(vis[c] != q + 1) ps[++cp] = c, vis[c] = q + 1;
}
sort(ps + 1, ps + cp + 1, cmp);
int rt = ps[1], dr = dep[rt];
M = 0;
for(int i = 1; i < cp; i++) {
int a = ps[i], b = ps[i+1], c = lca(a, b);
Add2(b, c, dep[b] - dep[c]);
if(dep[c] < dr) dr = dep[c], rt = c;
}
int tmp = siz[rt];
if(rt != 1) siz[rt] = n;
dp1(rt, 0);
dp2(rt, 0);
for(int i = 1; i <= cpi; i++) ans[psi[i]] = 0;
process(rt, 0);
siz[rt] = tmp;
if(cpi > 1){ for(int i = 1; i <= cpi; i++) printf("%d ", ans[Ps[i]]); putchar('\n'); }
else printf("%d\n", n);
} return 0;
}
[BZOJ3572][Hnoi2014]世界树的更多相关文章
- bzoj3572[Hnoi2014] 世界树 虚树+dp+倍增
[Hnoi2014]世界树 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1921 Solved: 1019[Submit][Status][Dis ...
- BZOJ3572 [Hnoi2014]世界树 【虚树 + 树形dp】
题目 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石. ...
- bzoj千题计划255:bzoj3572: [Hnoi2014]世界树
http://www.lydsy.com/JudgeOnline/problem.php?id=3572 明显需要构造虚树 点属于谁管理分三种情况: 1.属于虚树的点 2.在虚树上的边上的点 3.既不 ...
- 2018.09.25 bzoj3572: [Hnoi2014]世界树(虚树+树形dp)
传送门 虚树入门题? 好难啊. 在学习别人的写法之后终于过了. 这道题dp方程很好想. 主要是不好写. 简要说说思路吧. 显然最优值只能够从子树和父亲转移过来. 于是我们先dfs一遍用儿子更新父亲,然 ...
- 【BZOJ3572】[Hnoi2014]世界树 虚树
[BZOJ3572][Hnoi2014]世界树 Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森 ...
- 【BZOJ-3572】世界树 虚树 + 树形DP
3572: [Hnoi2014]世界树 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1084 Solved: 611[Submit][Status ...
- BZOJ 3572: [Hnoi2014]世界树
BZOJ 3572: [Hnoi2014]世界树 标签(空格分隔): OI-BZOJ OI-虚数 OI-树形dp OI-倍增 Time Limit: 20 Sec Memory Limit: 512 ...
- bzoj 3572: [Hnoi2014]世界树 虚树 && AC500
3572: [Hnoi2014]世界树 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 520 Solved: 300[Submit][Status] ...
- bzoj 3572 [Hnoi2014]世界树(虚树+DP)
3572: [Hnoi2014]世界树 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 645 Solved: 362[Submit][Status] ...
随机推荐
- jQuery关于隐式迭代的个人理解~
1.JQuery对象" 如: $('div').text("div展示的信息") 可以看成"是一个包含一个dom数组 和 包含所有Jquery方法的容器 2.每 ...
- 关键词提取1-C#
C# 中文分词算法(实现从文章中提取关键字算法) using System;using System.IO;using System.Text;using System.Collections;usi ...
- 9月23日JavaScript作业----两个列表之间移动数据
作业一:两个列表之间数据从一个列表移动到另一个列表 <div style="width:600px; height:500px; margin-top:20px"> & ...
- Java递归算法——三角数字
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.i ...
- python --- Python中的callable 函数
python --- Python中的callable 函数 转自: http://archive.cnblogs.com/a/1798319/ Python中的callable 函数 callabl ...
- Jquerymobile随笔
fixed <div data-role="header" data-position="fixed"> <h1>欢迎访问我的主页< ...
- xcode 不显示占用内存
解决办法: Scheme设置中,将 Enable Zombie Objects 勾选去掉.
- JAVA Applet
- 1.servlet的会话机制cookie
会话:用户开浏览器访问某个网站,只要不关闭浏览器,不管该用户点击多少个超链接,访问多少资源,直到用户关闭浏览器,整个过程称为一次会话 cookie会话: 1.记录用户上次登录的时间 2.浏览商品的历史 ...
- Java中native关键字
Java中native关键字 标签: Java 2016-08-17 11:44 54551人阅读 评论(0) 顶(23453) 收藏(33546) 今日在hibernate源代码中遇到了nati ...