@codeforces - 418D@ Big Problems for Organizers
@description@
n 个点连成一棵树,经过每条边需要花费 1 个单位时间。
现给出 m 次询问,每次询问给出两个点,需要求所有点同时出发,最终所有点到达这两个点之一的最小花费时间。
input
第一行包含一个整数 n (2 ≤ n ≤ 100000) ,表示点数。
接下来 n-1 行每行两个 1~n 的整数,描述一条边。
接下来包含一个整数 m,表示询问数。
接下来 m 行每行两个整数,表示我们每次询问的两个点。
output
对于每组询问,输出最少的时间。
Examples
Input1
3
2 3
3 1
3
2 1
2 3
3 1
Output1
1
1
1
Input2
4
1 4
1 2
2 3
3
1 4
1 3
2 3
Output2
2
1
2
@solution@
假设询问给出的是 u, v 两个点,每个点肯定是往 u, v 中离自己更近的点移动。
因此,我们总可以选择 u, v 的中心边,中心边连着的包含 u 的连通块向 u 移动,中心边连着的包含 v 的连通块向 v 移动。
假如偶数长度,有多个中心边,任取一条即可。
实际上,相当于将中心边割断,然后分别以 u, v 为根求最远点。
这个显然可以 lct 搞,不过我们还是不要搞那么复杂(其实也不复杂)。
考虑使用树链剖分能否维护(其实因为没有修改可以直接树上倍增)。不妨假设 u 的深度大于等于 v 的深度。
可以发现我们总可以找一条中心边使这个中心边在 u 到 lca(u, v) 的路径上。
因此,对于 u 来说,它的路径仅分为直接往下和先上后下两种。先上后下可以通过记录 -dep[x] + x不经过重儿子的最远点距离 的最大值即可。
而上面那个可以通过记录 x 向下的最长路径、次长路径即可得到,可以发现这样我们跳轻边时也容易得到答案。
而对于 v,我们需要分类讨论:
(1)假如 v 是 u 的祖先,v 的路径分为向上,向下但不经过中心边两种。这个时候我们再统计 dep[x] + x不经过重儿子的最远点距离 的最大值即可。
(2)否则,v 的路径分为直接往下、向上但不经过 lca 再向下、向上到达 lca 再向下、向上穿过 lca 继续向上、向上到达 lca 再转向 u 的方向向下但不经过中心边五种。
注意我们要把 “向上到达 lca 再向下” 这种情况单独提出来求解,因为 lca 向下有两条禁止通行的路径。此时我们还需要记录第三长的路径。
维护最大值直接写 st 表,可以做到 O((n + m)logn),因为树剖本身常数小所以跑得比 lct 和倍增都要快。
@accepted code@
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
const int INF = (1<<30);
struct edge{
edge *nxt; int to;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
int siz[MAXN + 5], dep[MAXN + 5], hvy[MAXN + 5], fa[MAXN + 5];
void dfs1(int x, int f) {
siz[x] = 1, dep[x] = dep[f] + 1, hvy[x] = 0, fa[x] = f;
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
dfs1(p->to, x);
siz[x] += siz[p->to];
if( siz[p->to] > siz[hvy[x]] )
hvy[x] = p->to;
}
}
int top[MAXN + 5], dfn[MAXN + 5], tid[MAXN + 5], dcnt = 0;
void dfs2(int x, int tp) {
top[x] = tp, dfn[++dcnt] = x, tid[x] = dcnt;
if( hvy[x] ) dfs2(hvy[x], tp);
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == fa[x] || p->to == hvy[x] ) continue;
dfs2(p->to, p->to);
}
}
int f[MAXN + 5], g[MAXN + 5], h[MAXN + 5];
int pf[MAXN + 5], pg[MAXN + 5];
void dfs3(int x) {
f[x] = g[x] = h[x] = 0;
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == fa[x] ) continue;
dfs3(p->to);
if( f[p->to] + 1 > f[x] ) {
h[x] = g[x], g[x] = f[x], f[x] = f[p->to] + 1;
pg[x] = pf[x], pf[x] = p->to;
}
else if( f[p->to] + 1 > g[x] ) {
h[x] = g[x], g[x] = f[p->to] + 1;
pg[x] = p->to;
}
else if( f[p->to] + 1 > h[x] )
h[x] = f[p->to] + 1;
}
}
int lca(int u, int v) {
while( top[u] != top[v] ) {
if( dep[top[u]] < dep[top[v]] ) swap(u, v);
u = fa[top[u]];
}
if( dep[u] < dep[v] ) swap(u, v);
return v;
}
int get_fa(int u, int d) {
while( dep[top[u]] > d )
u = fa[top[u]];
return dfn[tid[u] - (dep[u] - d)];
}
int st[2][20][MAXN + 5], lg[MAXN + 5];
void get_st() {
for(int i=1;i<=dcnt;i++) {
if( pf[dfn[i]] == hvy[dfn[i]] )
st[0][0][i] = g[dfn[i]] - dep[dfn[i]], st[1][0][i] = g[dfn[i]] + dep[dfn[i]];
else st[0][0][i] = f[dfn[i]] - dep[dfn[i]], st[1][0][i] = f[dfn[i]] + dep[dfn[i]];
}
for(int j=1;j<20;j++) {
int t = (1<<(j-1));
for(int i=1;i+t<=dcnt;i++)
st[0][j][i] = max(st[0][j-1][i], st[0][j-1][i+t]), st[1][j][i] = max(st[1][j-1][i], st[1][j-1][i+t]);
}
for(int i=2;i<=dcnt;i++)
lg[i] = lg[i>>1] + 1;
}
int rmq(int le, int ri, bool type) {
if( le > ri ) return -INF;
int k = lg[ri-le+1], l = (1<<k);
return max(st[type][k][le], st[type][k][ri-l+1]);
}
int query(int u, int v, bool type) {
int ret = -INF;
while( top[u] != top[v] ) {
ret = max(ret, rmq(tid[top[u]], tid[u]-1, type));
u = top[u];
if( pf[fa[u]] == u )
ret = max(ret, g[fa[u]] + (type ? 1 : -1)*dep[fa[u]]);
else ret = max(ret, f[fa[u]] + (type ? 1 : -1)*dep[fa[u]]);
u = fa[u];
}
return max(ret, rmq(tid[v], tid[u]-1, type));
}
int main() {
int n, m; scanf("%d", &n);
for(int i=1;i<n;i++) {
int u, v; scanf("%d%d", &u, &v);
addedge(u, v);
}
dfs1(1, 0), dfs2(1, 1), dfs3(1), get_st();
scanf("%d", &m);
for(int i=1;i<=m;i++) {
int a, b; scanf("%d%d", &a, &b);
if( dep[a] < dep[b] ) swap(a, b);
int l = lca(a, b), dis = dep[a] + dep[b] - 2*dep[l];
int mid = get_fa(a, dep[a] - (dis - 1)/2), ans = max(f[a], dep[a] + query(a, mid, 0));
if( b == l )
ans = max(ans, max(dep[b] + query(b, 1, 0), -dep[b] + query(mid, b, 1)));
else {
int p = get_fa(a, dep[l] + 1), q = get_fa(b, dep[l] + 1);
ans = max(ans, -dep[l] + query(mid, p, 1) + dep[b] - dep[l]);
ans = max(ans, dep[b] + query(b, q, 0));
ans = max(ans, dep[b] + query(l, 1, 0));
ans = max(ans, f[b]);
if( pf[l] == p ) {
if( pg[l] == q )
ans = max(ans, dep[b] - dep[l] + h[l]);
else ans = max(ans, dep[b] - dep[l] + g[l]);
}
else if( pf[l] == q ) {
if( pg[l] == p )
ans = max(ans, dep[b] - dep[l] + h[l]);
else ans = max(ans, dep[b] - dep[l] + g[l]);
}
else ans = max(ans, dep[b] - dep[l] + f[l]);
}
printf("%d\n", ans);
}
}
@details@
总之各种分类讨论还是非常令人心烦的。。。
这个可能真的要写对拍,不然就得反复看自己有没有漏情况,或者是某种情况的公式推错了什么的。。。
@codeforces - 418D@ Big Problems for Organizers的更多相关文章
- Codeforces 418d Big Problems for Organizers [树形dp][倍增lca]
题意: 给你一棵有n个节点的树,树的边权都是1. 有m次询问,每次询问输出树上所有节点离其较近结点距离的最大值. 思路: 1.首先是按照常规树形dp的思路维护一个子树节点中距离该点的最大值son_di ...
- Big Problems for Organizers CodeForces - 418D (贪心,直径)
大意: 给定n结点树, m个询问, 每次给出两个旅馆的位置, 求树上所有结点到最近旅馆距离的最大值 先考虑一些简单情形. 若旅馆只有一个的话, 显然到旅馆最远的点是直径端点之一 若树为链的话, 显然是 ...
- codeforces 673B B. Problems for Round(模拟)
题目链接: B. Problems for Round time limit per test 2 seconds memory limit per test 256 megabytes input ...
- CF418D Big Problems for Organizers 树的直径、ST表
题目传送门:http://codeforces.com/problemset/problem/418/D 大意:给出一棵有$N$个节点的树,所有树边边权为$1$,给出$M$次询问,每个询问给出$x,y ...
- CF418D Big Problems for Organizers
传送门 题意,给一棵树,每次给两个点\(x,y\),求\(\max_{i=1}^{n}(\min(di_{x,i},di_{y,i}))\) 看std看了好久 以下是一个优秀的在线做法,\(O(nlo ...
- [JZOJ3690] 【CF418D】Big Problems for Organizers
题目 题目大意 给你一棵树,然后有一堆询问,每次给出两个点. 问所有点到两个点中最近点的距离的最大值. 正解 本来打了倍增,然后爆了,也懒得调-- 显然可以在两个点之间的路径的中点处割开,一边归一个点 ...
- Codeforces Round #351 (VK Cup 2016 Round 3, Div. 2 Edition) B. Problems for Round 水题
B. Problems for Round 题目连接: http://www.codeforces.com/contest/673/problem/B Description There are n ...
- codeforces 727F. Polycarp's problems
题目链接:http://codeforces.com/contest/727/problem/F 题目大意:有n个问题,每个问题有一个价值ai,一开始的心情值为q,每当读到一个问题时,心情值将会加上该 ...
- Codeforces 913D - Too Easy Problems
913D - Too Easy Problems 思路:二分check k 代码: #include<bits/stdc++.h> using namespace std; #define ...
随机推荐
- NSIS之MUI
NSIS 2.0 版本支持定制的用户界面.所谓的 Modern UI(下称 MUI) 就是一种模仿最新的 Windows 界面风格的界面系统.MUI 改变了 NSIS 脚本的编写习惯,它使用 NSIS ...
- ES6 新的数据类型Map和Set
Map Map是一组键值对的结构,具有极快的查找速度. 举个例子,假设要根据同学的名字查找对应的成绩,如果用Array实现,需要两个Array: var names = ['Michael', 'Bo ...
- 推荐大家自学的java学习网站,生动的讲解适合刚入门
java学习网站(不仅仅是只学习java的知识):http://how2j.cn 首先大家来看看这个网站都有些啥 首页:图中的左侧目录大家看到了,从java基础到高级,从后台技术到前端页面,数据库,还 ...
- php数字转人民币金额大写
numToRmb.php <?php header("content-type:text/html;charset=utf-8"); function numToRmb($n ...
- Oracle启动和禁用约束及删除违反约束的记录
一.禁用约束 alter table table_name disable novalidate constraint constraint_name 二.批量导入数据 三.在开启约束之前一定要检查违 ...
- git出现“The file will have its original line endings in your working directory”错误
一.现象: git add *时出现如下现象: The file will have its original line endings in your working directory 解决: G ...
- 洛谷 P2356 弹珠游戏
题目链接:https://www.luogu.org/problemnew/show/P2356 题目 题目描述 MedalPluS 和他的小伙伴 NOIRP 发掘了一个骨灰级别的游戏——超级弹珠. ...
- python变量、变量属性
1.简述执行Python程序的两种方式以及他们的优缺点 答:交互型优点:马上就能看到结果,排错方便.交互型缺点:代码无法保存,断电即消失 命令型优点:代码永久保存.命令型缺点:不易排错 2.简述Pyt ...
- 基于mcp940反编译Minecraft源代码
引言 Minecraft中文叫"我的世界",没怎么深入玩过,来试试把它源代码反编译出来吧. 参考教程: https://minecraft.gamepedia.com/Mods/C ...
- tomcat的日志不输出日志信息的解决方法
1.下载日志jar包,例如:commons-logging-1.1.1.jar.放在tomcat的bin目录下,或者是自己项目的lib包里. 2.修改tomcat的bin目录下面的catalina.b ...