题目描述

给你一棵 $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. C++默认成员函数

    1.什么是面向对象? 概念:(Object Oriented Programming,缩写:OOP)是一种程序设计范型,同时也是一种程序开发的方法. 对象指的是类的实例,将对象作为程序的基本单元,将程 ...

  2. while循环计算规则:内循环—外循环!

    num= 1 #值 =1while num <= 10 : # num(1)小于10 print(num) # 应该打印 这个1的值 num +=1 # num+=1等价于 num再加1 所以这 ...

  3. 《杜增强讲Unity之Tanks坦克大战》8-子弹碰撞处理

    8 子弹碰撞处理 为了处理子弹打到坦克的伤害我们在这里新建一个Shell.cs 子弹有两种情况,碰到坦克炸开,没有碰到坦克则过2s子弹销毁. void Start () { Destroy (game ...

  4. python的eval和json.loads(),json.dumps()

    eval() 将字符串当成一个表达式去执行,可以想象成一个去字符串然后执行的操作. In [1]: s = '3*8' In [2]: eval(s) Out[2]: 24 eval()和json.l ...

  5. MAC node + git + bower 简单安装

    一 node 安装 打开https://nodejs.org/en/ nodejs官网 下载安装文件 双击.pkg 文件 自动安装即可 二 安装git 打开 http://code.google.co ...

  6. Mysql读写分离——主从数据库+Atlas

    mysql集群 最近在参加项目开发微信小程序后台,由于用户数量巨大,且后台程序并不是很完美,所以对用户的体验很是不友好(简单说就是很卡).赶巧最近正在翻阅<大型网站系统与Java中间件实践> ...

  7. CUDA、CUDNN在Mac Book Pro上安装的问题

    由于原版MacOS自带Nvidia驱动版本过低,导致最新版本CUDA安装后无法运行.具体症状为:在编译时一切正常,在运行CUDA相关程序时报错: CUDA driver version is insu ...

  8. UITabBarController的使用

    UITabBarController的使用 前言: 苹果开发的小伙伴都知道,项目中只要用到了UITabBarController,UITabBarController就是APP的骨架.所以熟练掌握UI ...

  9. proxyTable中pathWrrite的使用

    proxyTable中pathWrrite的使用 proxyTable: { '/iclient': { target: 'http://xxx.xx.com/iclient/xx/xx', chan ...

  10. 初步认识CNN

    1.机器学习 (1)监督学习:有数据和标签 (2)非监督学习:只有数据,没有标签 (3)半监督学习:监督学习+非监督学习 (4)强化学习:从经验中总结提升 (5)遗传算法:适者生存,不适者淘汰 2.神 ...