题意:给定树上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. 以英雄联盟的方式建模,谈对依赖注入(DI)的理解以及Autofac的用法(一)

    一.前言 近期在探索分层架构和架构设计,选择了领域驱动作为5年.Net开发后的新的方向,不可避免的接触了IoC/DI方面的技术.目前通过反射或其他方法都已实现,但只知其一,并没有考虑为什么要这么做,同 ...

  2. 自动化批量管理工具salt-ssh - 运维小结

    根据以往运维工作中操作经验来说,当管理上百台上千台服务器时,选择一款批量操作工具是及其有必要的.早期习惯于在ssh信任关系的前提下做for;do;done循环语句的批量操作,后来逐渐趋于使用批量工具操 ...

  3. sheet制作返回按钮

    =HYPERLINK("#目录!A1","目录!A1") =HYPERLINK("#"&A2&"!A1" ...

  4. Linux内核及分析 第五周 扒开系统调用的三层皮(下)

    实验内容: 1.执行rm menu -rf命令,强制删除原有的menu 2.使用git命令 git clone https://github.com/mengning/menu.git 克隆新的men ...

  5. LINUX内核分析第五周学习总结——扒开系统调用的“三层皮”(下)

    LINUX内核分析第五周学习总结--扒开系统调用的"三层皮"(下) 标签(空格分隔): 20135321余佳源 余佳源 原创作品转载请注明出处 <Linux内核分析>M ...

  6. Struts2中的图片验证码

    1.Struts中建一个action <action name="Code" class="LoginAction" method="code& ...

  7. TCP报文格式详解

    TCP报文是TCP层传输的数据单元,也叫报文段. 1.端口号:用来标识同一台计算机的不同的应用进程. 1)源端口:源端口和IP地址的作用是标识报文的返回地址. 2)目的端口:端口指明接收方计算机上的应 ...

  8. JS判断浏览器种类

    function myBrowser() {                        var userAgent = navigator.userAgent; //取得浏览器的userAgent ...

  9. jquery的extend方法(源码解析)

    1.前段时间一直忙于研究数据可视化(d3.js,three.js) 以及 php的 laravel框架,生活上也遇到很多事情,这大概就是人生中的迷茫期吧. 回想起,刚出来工作的时候,目标很明确,要学习 ...

  10. node的经典事件监听

    let fs = require('fs'); let Event = require('events'); let myEvent = new Event(); //注册一个订阅者 A myEven ...