题意:给定树上k个关键点,每个点属于离他最近,然后编号最小的关键点。求每个关键点管辖多少点。

解:虚树 + DP。

虚树不解释。主要是DP。用二元组存虚树上每个点的归属和距离。这一部分是二次扫描与换根法。

然后把关键点改为虚树节点,统计每个虚树节点管辖多少个节点,用SIZ表示,初始时SIZ = siz,SIZ[RT] = n。

如果一条虚树边两端点的归属相同。那么SIZ[fa] -= siz[son]

否则树上倍增找到y是最靠上属于的son的,然后SIZ[fa] -= siz[y] SIZ[son] = siz[y]

 #include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring> typedef long long LL;
const int N = , INF = 0x3f3f3f3f; struct Edge {
int nex, v, len;
}edge[N * ], EDGE[N * ]; int tp, TP; struct Node {
int x, d;
Node(int X = , int D = ) {
x = X;
d = D;
}
}small[N]; int e[N], E[N], RT, siz[N], d[N], num, pos[N], pw[N], Time, imp[N], stk[N], top, now[N], imp2[N];
int ans[N], fr[N], SIZ[N], n, fa[N][], use[N]; inline void add(int x, int y) {
tp++;
edge[tp].v = y;
edge[tp].nex = e[x];
e[x] = tp;
return;
} inline void ADD(int x, int y) {
// printf("ADD %d %d \n", x, y);
TP++;
EDGE[TP].v = y;
EDGE[TP].len = d[y] - d[x];
EDGE[TP].nex = E[x];
E[x] = TP;
return;
} inline bool cmp(const int &a, const int &b) {
return pos[a] < pos[b];
} void DFS_1(int x, int father) {
fa[x][] = father;
d[x] = d[father] + ;
siz[x] = ;
pos[x] = ++num;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(y == father) {
continue;
}
DFS_1(y, x);
siz[x] += siz[y];
}
return;
} inline int lca(int x, int y) {
if(d[x] > d[y]) {
std::swap(x, y);
}
int t = pw[n];
while(t >= && d[x] < d[y]) {
if(d[fa[y][t]] >= d[x]) {
y = fa[y][t];
}
t--;
}
if(x == y) {
return x;
}
t = pw[n];
while(t >= && fa[x][] != fa[y][]) {
if(fa[x][t] != fa[y][t]) {
x = fa[x][t];
y = fa[y][t];
}
t--;
}
return fa[x][];
} inline void clear(int x) {
if(use[x] != Time) {
use[x] = Time;
E[x] = ;
}
return;
} inline void build_t(int k) {
std::sort(imp + , imp + k + , cmp);
TP = top = ;
clear(imp[]);
stk[++top] = imp[];
for(int i = ; i <= k; i++) {
int x = imp[i], y = lca(stk[top], x);
clear(x); clear(y);
while(top > && pos[y] <= pos[stk[top - ]]) {
ADD(stk[top - ], stk[top]);
top--;
}
if(y != stk[top]) {
ADD(y, stk[top]);
stk[top] = y;
}
stk[++top] = x;
}
while(top > ) {
ADD(stk[top - ], stk[top]);
top--;
}
RT = stk[top];
return;
} void out_t(int x) {
printf("out x = %d \n", x);
for(int i = E[x]; i; i = EDGE[i].nex) {
int y = EDGE[i].v;
// printf("EDGE %d y %d \n", i, y);
out_t(y);
}
return;
} void getSmall(int x) {
(now[x] == Time) ? small[x] = Node(x, ) : small[x] = Node(n + , INF);
// printf("getSmall x = %d small = %d \n", x, small[x].x);
SIZ[x] = siz[x];
for(int i = E[x]; i; i = EDGE[i].nex) {
int y = EDGE[i].v;
getSmall(y);
if(small[x].d > small[y].d + EDGE[i].len) {
small[x] = small[y];
small[x].d += EDGE[i].len;
}
else if(small[x].d == small[y].d + EDGE[i].len) {
small[x].x = std::min(small[x].x, small[y].x);
}
}
return;
} void getEXsmall(int x, Node t) {
// printf("EX x = %d small = %d \n", x, small[x].x);
if(small[x].d > t.d || (small[x].d == t.d && small[x].x > t.x)) {
small[x] = t;
}
// printf("x = %d small = %d \n", x, small[x].x);
for(int i = E[x]; i; i = EDGE[i].nex) {
int y = EDGE[i].v;
getEXsmall(y, Node(small[x].x, small[x].d + EDGE[i].len));
}
return;
} inline int getPos(int x, int f) {
int t = pw[d[x] - d[f]], y = x;
while(t >= ) {
int mid = fa[y][t];
if(d[x] - d[mid] + small[x].d < d[mid] - d[f] + small[f].d) {
y = mid;
}
else if(d[x] - d[mid] + small[x].d == d[mid] - d[f] + small[f].d && small[f].x > small[x].x) {
y = mid;
}
t--;
}
return y;
} void del(int x, int f) {
// printf("del x = %d small = %d %d \n", x, small[x].x, small[x].d);
if(f) {
if(small[x].x == small[f].x) {
SIZ[f] -= siz[x];
// printf("SIZ %d -= %d = %d \n", f, siz[x], SIZ[f]);
}
else {
int y = getPos(x, f);
SIZ[f] -= siz[y];
// printf("SIZ %d -= %d = %d \n", f, siz[y], SIZ[f]);
SIZ[x] = siz[y];
// printf("SIZ %d = siz %d %d \n", x, y, siz[y]);
}
}
for(int i = E[x]; i; i = EDGE[i].nex) {
int y = EDGE[i].v;
del(y, x);
}
ans[small[x].x] += SIZ[x];
return;
} inline void solve() {
int k;
scanf("%d", &k);
Time++;
for(int i = ; i <= k; i++) {
scanf("%d", &imp[i]);
now[imp[i]] = Time;
ans[imp[i]] = ;
}
memcpy(imp2 + , imp + , k * sizeof(int));
build_t(k); // out_t(RT);
// get Small
getSmall(RT); // get Size
getEXsmall(RT, Node(n + , INF));
//
SIZ[RT] = n;
del(RT, );
for(int i = ; i <= k; i++) {
printf("%d ", ans[imp2[i]]);
}
printf("\n");
return;
} int main() { // freopen("in.in", "r", stdin);
// freopen("a.out", "w", stdout); scanf("%d", &n);
for(int i = , x, y; i < n; i++) {
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
DFS_1(, );
for(int i = ; i <= n; i++) {
pw[i] = pw[i >> ] + ;
}
for(int j = ; j <= pw[n]; j++) {
for(int i = ; i <= n; i++) {
fa[i][j] = fa[fa[i][j - ]][j - ];
}
}
int q;
scanf("%d", &q);
while(q--) {
solve();
}
return ;
}

AC代码

洛谷P3233 世界树的更多相关文章

  1. 洛谷P3233 世界树 [HNOI2014] 虚树

    正解:虚树 解题报告: 传送门! 首先看到这种就要想到虚树这个是毫无疑问的QwQ 建虚树什么的都可以循规蹈矩地做,不说辣,具体可以看下虚树学习笔记什么的看下板子 但是建好虚树之后怎么搞还是有点儿讲究, ...

  2. ●洛谷P3233 [HNOI2014]世界树

    题链: https://www.luogu.org/problemnew/show/P3233题解: 虚树,dp,倍增. 首先对于每个询问,要把虚树建出来,这一步就从略了.这里着重分享一下如何求答案. ...

  3. 洛谷P3233 [HNOI2014]世界树

    虚树= = #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring&g ...

  4. 洛谷 P3233 [HNOI2014]世界树(虚树+dp)

    题面 luogu 题解 数据范围已经告诉我们是虚树了,考虑如何在虚树上面\(dp\) 以下摘自hzwer博客: 构建虚树以后两遍dp处理出虚树上每个点最近的议事处 然后枚举虚树上每一条边,考虑其对两端 ...

  5. 洛谷1640 bzoj1854游戏 匈牙利就是又短又快

    bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...

  6. 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.

    没有上司的舞会  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond       题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...

  7. 洛谷P1108 低价购买[DP | LIS方案数]

    题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...

  8. 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn Label:二维数组前缀和 你够了 这次我用DP

    题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...

  9. 洛谷P1710 地铁涨价

    P1710 地铁涨价 51通过 339提交 题目提供者洛谷OnlineJudge 标签O2优化云端评测2 难度提高+/省选- 提交  讨论  题解 最新讨论 求教:为什么只有40分 数组大小一定要开够 ...

随机推荐

  1. LeetCode Pow(x, n) (快速幂)

    题意 Implement pow(x, n). 求X的N次方. 解法 用正常的办法来做是会超时的,因为可能有21亿次方的情况,所以需要优化一下.这里用到了快速幂算法,简单来说就是将指数分解成二进制的形 ...

  2. Android恶意样本数据集汇总

    硕士论文的研究方向为Android恶意应用分类,因此花了一点时间去搜集Android恶意样本.其中一部分来自过去论文的公开数据集,一部分来自社区或平台的样本.现做一个汇总,标明了样本或数据集的采集时间 ...

  3. libmysqlclient.so.16: cannot open shared object file: No such file or directory

    编译安装的mysql5.6.39,安装目录是/usr/local/mysql,启用程序时报错:libmysqlclient.so.16: cannot open shared object file: ...

  4. hadoop-mapreduce-(1)-统计单词数量

    编写map程序 package com.cvicse.ump.hadoop.mapreduce.map; import java.io.IOException; import org.apache.h ...

  5. C++ string简单的使用技巧

    截取substr //string的操作 #include<iostream> using namespace std; int main() { string a,b; a=" ...

  6. 基于 CentOS 搭建 FTP 文件服务

    https://www.linuxidc.com/Linux/2017-11/148518.htm

  7. LINUX内核分析第八周总结:进程的切换和系统的一般执行过程

    一.进程调度与进程切换 1.不同的进程有不同的调度需求 第一种分类: I/O密集型(I/O-bound) 频繁的进行I/O 通常会花费很多时间等待I/O操作的完成 CPU密集型(CPU-bound) ...

  8. 《蹭课神器》Beta版使用说明

    相比 Alpha 版,我对主界面进行了优化,使主界面更加简洁 同时数据库增加了一个表,里面存放的是课程的详细信息

  9. answer my questions from the book<构建之法>.

    1)何为文档:文档时在一个项目进行的一生中所有记忆的集合.有需求分析.功能设计.在实现功能过程中也可以有一系列文档记录.测试文档等等. 2)结对工作等找队友会花费大量时间致耽误项目否:正如老师所讲,从 ...

  10. 过滤器Filter的使用(以登录为例子)

    使用过滤器步骤: (1)在web.xml文件中添加过滤器(以下例子是过滤多个请求) <!-- 用户登录过滤 --> <filter> <filter-name>lo ...