题目描述

给你一棵 $n$ 个点的树,边有边权。$m$ 次询问,每次给出 $l$ 、$r$ 、$x$ ,求 $\text{Min}_{i=l}^r\text{dis}(i,x)$ 。

$n,m\le 10^5$ 。


题解

动态点分治+线段树

分块做法太傻逼了我们把它丢到垃圾桶里。树上距离考虑动态点分治。

求出这棵树的点分树,对每一棵点分树子树开一棵动态开点编号线段树,维护编号在某区间内的点到当前点距离的最大值。

对于一次查询,我们在点分树从 $x$ 到根的路径上所有点对应的线段树上查询 $[l,r]$ 的最大值,$dis(i,x)+query(l,r,root_i)$ 的最大值极为答案。

这样做的正确性比较显然:

1. 每个 $[l,r]$ 内的点都属于这些子树的一个部分内,都被正确统计了一次。

2. 多余统计时,距离只会统计大,不会统计小,没有影响。

时间复杂度 $O(n\log^2 n)$

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
#define inf 1 << 30
using namespace std;
int head[N] , to[N << 1] , len[N << 1] , next[N << 1] , cnt , deep[N] , pos[N] , md[N << 1][20] , log[N << 1] , tot , si[N] , ms[N] , sum , root , vis[N] , fa[N];
int ls[N * 300] , rs[N * 300] , mn[N * 300] , rt[N] , tp;
inline void add(int x , int y , int z)
{
to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x , int pre)
{
int i;
md[++tot][0] = deep[x] , pos[x] = tot;
for(i = head[x] ; i ; i = next[i])
if(to[i] != pre)
deep[to[i]] = deep[x] + len[i] , dfs(to[i] , x) , md[++tot][0] = deep[x];
}
inline int dis(int x , int y)
{
int t = deep[x] + deep[y] , k;
x = pos[x] , y = pos[y];
if(x > y) swap(x , y);
k = log[y - x + 1];
return t - 2 * min(md[x][k] , md[y - (1 << k) + 1][k]);
}
void getroot(int x , int pre)
{
int i;
si[x] = 1 , ms[x] = 0;
for(i = head[x] ; i ; i = next[i])
if(!vis[to[i]] && to[i] != pre)
getroot(to[i] , x) , si[x] += si[to[i]] , ms[x] = max(ms[x] , si[to[i]]);
ms[x] = max(ms[x] , sum - si[x]);
if(ms[x] < ms[root]) root = x;
}
void solve(int x)
{
int i;
vis[x] = 1;
for(i = head[x] ; i ; i = next[i])
if(!vis[to[i]])
sum = si[to[i]] , root = 0 , getroot(to[i] , 0) , fa[root] = x , solve(root);
}
void update(int p , int a , int l , int r , int &x)
{
if(!x) x = ++tp , mn[x] = inf;
mn[x] = min(mn[x] , a);
if(l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) update(p , a , l , mid , ls[x]);
else update(p , a , mid + 1 , r , rs[x]);
}
int query(int b , int e , int l , int r , int x)
{
if(!x) return inf;
if(b <= l && r <= e) return mn[x];
int mid = (l + r) >> 1 , ans = inf;
if(b <= mid) ans = min(ans , query(b , e , l , mid , ls[x]));
if(e > mid) ans = min(ans , query(b , e , mid + 1 , r , rs[x]));
return ans;
}
int main()
{
int n , m , i , j , x , y , z , ans;
scanf("%d" , &n);
for(i = 1 ; i < n ; i ++ ) scanf("%d%d%d" , &x , &y , &z) , add(x , y , z) , add(y , x , z);
dfs(1 , 0);
for(i = 2 ; i <= tot ; i ++ ) log[i] = log[i >> 1] + 1;
for(i = 1 ; i <= log[tot] ; i ++ )
for(j = 1 ; j <= tot - (1 << i) + 1 ; j ++ )
md[j][i] = min(md[j][i - 1] , md[j + (1 << (i - 1))][i - 1]);
ms[0] = sum = n , root = 0 , getroot(1 , 0) , solve(root);
for(i = 1 ; i <= n ; i ++ )
for(j = i ; j ; j = fa[j])
update(i , dis(i , j) , 1 , n , rt[j]);
scanf("%d" , &m);
while(m -- )
{
scanf("%d%d%d" , &x , &y , &z) , ans = inf;
for(i = z ; i ; i = fa[i]) ans = min(ans , dis(i , z) + query(x , y , 1 , n , rt[i]));
printf("%d\n" , ans);
}
return 0;
}

【loj6145】「2017 山东三轮集训 Day7」Easy 动态点分治+线段树的更多相关文章

  1. #6145. 「2017 山东三轮集训 Day7」Easy 动态点分治

    \(\color{#0066ff}{题目描述}\) JOHNKRAM 最近在参加 C_SUNSHINE 举办的聚会. C 国一共有 n 座城市,这些城市由 n−1 条无向道路连接.任意两座城市之间有且 ...

  2. 「2017 山东三轮集训 Day7」Easy

    一棵带边权的树,多次询问 $x$ 到编号为 $[l,r]$ 的点最短距离是多少 $n \leq 100000$ sol: 动态点分治,每层重心维护到所有点的距离 查询的时候在管辖这个点的 log 层线 ...

  3. LOJ #6145. 「2017 山东三轮集训 Day7」Easy 点分树+线段树

    这个就比较简单了~ Code: #include <cstdio> #include <algorithm> #define N 100004 #define inf 1000 ...

  4. 「2017 山东三轮集训 Day7 解题报告

    「2017 山东三轮集训 Day7」Easy 练习一下动态点分 每个点开一个线段树维护子树到它的距离 然后随便查询一下就可以了 注意线段树开大点... Code: #include <cstdi ...

  5. 「2017 山东三轮集训 Day1」Flair

    模拟赛的题 好神仙啊 题面在这里 之前的Solution很蠢 现在已经update.... 题意 有$ n$个商品价格均为$ 1$,您有$ m$种面值的货币,面值为$ C_1..C_m$ 每种物品你有 ...

  6. 【loj6142】「2017 山东三轮集训 Day6」A 结论题+Lucas定理

    题解: 当奇数 发现答案就是C(n,1)^2+C(n,3)^2+...C(n,n)^2 倒序相加,发现就是C(2n,n) 所以答案就是C(2n,n)/2 当偶数 好像并不会证 打表出来可以得到 2.当 ...

  7. loj #6138. 「2017 山东三轮集训 Day4」Right

    题目: 题解: 暴力一波 \(SG\) 函数可以发现这么一个规律: \(p\) 为奇数的时候 : \(SG(n) = n \% 2\) \(p\) 为偶数的时候 : \(SG(n) = n \% (p ...

  8. loj #6136. 「2017 山东三轮集训 Day4」Left

    题目: 题解: 我们可以发现所有的交换器都是一个位置连接着下一层左侧的排序网络,另一个位置连着另一侧的排序网络. 而下一层是由两个更低阶的排序网络构成的. 两个网络互不干扰.所以我们可以通过第一行和最 ...

  9. Loj #6142. 「2017 山东三轮集训 Day6」A

    link: https://loj.ac/problem/6142 推完一波式子之后发现求的是:ΣC(N,i)^2, 其中i是偶数. 然后就可以卢卡斯乱搞了,分奇偶和之前的答案合并就好了233. #i ...

随机推荐

  1. 【Java字符序列】Pattern

    简介 Pattern,正则表达式的编译表示,操作字符序列的利器. 整个Pattern是一个树形结构(对应于表达式中的‘|’),一般为链表结构,树(链表)的基本元素是Node结点,Node有各种各样的子 ...

  2. Linux入门基础(七):Linux软件管理基础

    源代码形式 绝大多数开源软件都是直接以源代码形式发布 源代码一般会被打包成tar.gz的归档压缩文件 程序源代码需要编译成为二进制形式之后才能够运行 源代码基本编译流程 : ./configure 检 ...

  3. java练习(一)数组、集合的运用

    有这么一个有趣的问题,问:有这么一个不重复的自然数数组,自然数长度为N,而数组长度为N-2,依次随机把自然数放进数组中,请找出2个没有被放进去的自然数.例如:这个自然数数组是[0, 1, 2, 3,  ...

  4. WebGL——osg框架学习四

    这篇我们接着来看一下DrawEntityActor类,我们来看看这个继承DrawActor的类到底做了什么事.我们之前学习了Drawable对应的DrawActor,那么我们类比的来看Drawable ...

  5. 虚拟机中安装MAC OS X教程(适用所有电脑方法,特别是cpu不支持硬件虚拟化的电脑)

    前言 之前写了一篇在Windows上搭建Object-C开发环境,并且写了一个HelloWorld程序.但真正开发苹果软件是在MAC OS X系统中(以下简称OSX)中.买不起MacBook,也没有O ...

  6. http跳转https方法:百度云如何让http自动跳转到https【免费SSL证书使用FAQ】

    之前的一篇文章已经给大家提供了免费SSL证书的申请方法,这一篇文章是告诉大家在使用免费的SSL证书时可能会遇到的问题[怎么让http自动跳转到https以及http与https同时使用]的解决方法. ...

  7. 深度学习-tensorflow学习笔记(1)-MNIST手写字体识别预备知识

    深度学习-tensorflow学习笔记(1)-MNIST手写字体识别预备知识 在tf第一个例子的时候需要很多预备知识. tf基本知识 香农熵 交叉熵代价函数cross-entropy 卷积神经网络 s ...

  8. Final发布用户使用报告 -- Thunder团队

    Thunder爱阅app Final发布用户使用报告 用户数量:14人 以下为用户评论:(注:为了保护用户的姓名权,以下用户名以昵称形式给出.) 序列 昵称 个人信息 获得软件途径 使用次数 用户评论 ...

  9. TeamWork#3,Week5,Scrum Meeting 11.4

    今天我们进行了第一次Scrum Meeting,总结了最近一段时间的工作成果和经验教训,并分配了每个成员下一步的工作.网络爬虫对我们来说是一个难点,因为之前接触比较少,所以需要从头学起.我们参考了大量 ...

  10. 20162328蔡文琛 week10 大二

    20162328 2017-2018-1 <程序设计与数据结构>第十周学习总结 教材学习内容总结 理解图与有向图.无向图 理解带权图 会应用带权图 理解图的广度优先遍历和深度优先遍历 掌握 ...