CodeChef TRIPS-Children Trips 树上分块
参考文献国家集训队2015论文《浅谈分块在一类在线问题的应用》-邹逍遥
题目大意
一棵n个节点的树,树的每条边长度为1或2,每次询问x,y,z。
要求输出从x开始走,每次只能走到当前节点距离$\le z$的点,问最少几次能走到y
大致思路
考虑将树进行深度分块,设$size=\sqrt{n}$,对于每个节点x,如果$depth[x]\%size==1$则称它是关键点。
于是这棵树就被这些关键点分成了若干块(关键点属于它下面的块),如果某一块的大小小于size,就把它和上一个块合并。
这样这棵树的每个块大小就$\ge size$,块的个数就$\le size$,并且每个块的直径$\le size*4$,可以在$\sqrt{n}$的时间求出每个询问
具体实现
对于每个节点,预处理它到上面的块中离自己最近的节点(一定是他的祖先)的距离和在z($z\le size*2$)
(当$z>size$时可以一步跨过一个块)的情况下要走多少步,最后一步还剩下多长,走z后到达的节点,
以及每个节点的父亲,和到父亲节点的距离(为在块中暴力准备)
对于每个询问,如果当前两个点不在同一个块,则所在块靠下的点移动到上面的块中离自己最近的节点
如果在同一个块,则暴力让深度深的点移到它的父亲
总复杂度$O(n\sqrt{n})$
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 100005
#define maxs 350
struct Edge{
int next,to,w;
}edge[maxn*];
int n,size,fi[maxn],se,stack[maxn],top,depth[maxn],fa[maxn],block[maxn],s,remain[maxn][maxs*],step[maxn][maxs*],key[maxs],dis[maxn];
int reach[maxn][maxs*],dis1[maxn];
inline void add_edge(int u,int v,int w){
edge[++se].next=fi[u],fi[u]=se,edge[se].to=v,edge[se].w=w,
edge[++se].next=fi[v],fi[v]=se,edge[se].to=u,edge[se].w=w;
}
int dfs(int x){//第一次dfs分块,预处理出fa(父亲),dis(到父亲的距离)
int si=,k;
stack[top++]=x;
for(int i=fi[x];i;i=edge[i].next){
int v=edge[i].to;
if(v==fa[x])continue;
dis[v]=edge[i].w,fa[v]=x,depth[v]=depth[x]+,si+=dfs(v);
}
if(depth[x]%size==&&si>=size){
k=++s;block[x]=k;key[k]=x;
while(stack[--top]!=x){
block[stack[top]]=k;
}
}
return si;
}
void dfs1(int x){//第二次dfs预处理reach[x][z](走z距离能到达的点),dis1(到最近的不在一个块的祖先有多少距离)step(要走几步),remain(还剩多少距离)
reach[x][]=x;
reach[x][]=dis[x]==?fa[x]:x;
for(int i=;i<=size*;i++){
if(i-dis[x]>=)reach[x][i]=reach[fa[x]][i-dis[x]];
else reach[x][i]=fa[x];
}
if(block[x]==block[fa[x]]){
int v;
for(int i=;i<=size*;i++){
if(block[x]==block[v=reach[x][i]]){
step[x][i]=step[v][i]+;
remain[x][i]=remain[v][i];
}
else{
step[x][i]=;remain[x][i]=remain[fa[x]][i]-dis[x];
}
}
dis1[x]=dis1[fa[x]]+dis[x];
}
else{
for(int i=;i<=size*;i++){
step[x][i]=;remain[x][i]=i-dis[x];
}
dis1[x]=dis[x];
}
for(int i=fi[x];i;i=edge[i].next){
int v=edge[i].to;
if(v!=fa[x])dfs1(v);
}
}
int query(int l,int r,int p){//对于p<=size*2的询问
int ans=,sup1=,sup2=;
while(block[l]!=block[r]){//如果两个点不在一个块
if(block[l]<block[r]){
if(dis1[l]>sup1)l=reach[l][sup1],ans+=step[l][p],sup1=remain[l][p],l=fa[key[block[l]]];
else sup1=remain[l][sup1],l=fa[key[block[l]]];
}
else{
if(dis1[r]>sup2)r=reach[r][sup2],ans+=step[r][p],sup2=remain[r][p],r=fa[key[block[r]]];
else sup2=remain[r][sup2],r=fa[key[block[r]]];
}
}
while(l!=r){//在一个块后就暴力走
if(depth[l]>depth[r]){
if(sup1>=dis[l])sup1-=dis[l],l=fa[l];
else sup1=p-dis[l],ans++,l=fa[l];
}
else {
if(sup2>=dis[r])sup2-=dis[r],r=fa[r];
else sup2=p-dis[r],ans++,r=fa[r];
}
}
if(sup1+sup2>=p)ans--;
return ans;
}
int query1(int l,int r,int p){//处理p>size*2的询问
int ans=,sup1=,sup2=;
while(block[l]!=block[r]){
if(block[l]<block[r]){
if(dis1[l]<=sup1)sup1-=dis1[l],l=fa[key[block[l]]];
else{
if(sup1>(size<<)){
if(reach[l][size<<]==reach[l][(size<<)-])sup1-=(size<<)-;
else sup1-=(size<<);
l=reach[l][size<<];
}
else{
l=reach[l][sup1],ans++,sup1=p;
}
}
}
else{
if(dis1[r]<=sup2)sup2-=dis1[r],r=fa[key[block[r]]];
else{
if(sup2>(size<<)){
if(reach[r][size<<]==reach[r][(size<<)-])sup2-=(size<<)-;
else sup2-=(size<<);
r=reach[r][size<<];
}
else{
r=reach[r][sup2],ans++,sup2=p;
}
}
}
}
while(l!=r){
if(depth[l]>depth[r]){
if(sup1>=dis[l])sup1-=dis[l],l=fa[l];
else sup1=p-dis[l],ans++,l=fa[l];
}
else {
if(sup2>=dis[r])sup2-=dis[r],r=fa[r];
else sup2=p-dis[r],ans++,r=fa[r];
}
}
if(sup1+sup2>=p)ans--;
return ans;
}
int main(){
int u,v,w,m;
scanf("%d",&n);size=sqrt(n);
for(int i=;i<n;i++)scanf("%d%d%d",&u,&v,&w),add_edge(u,v,w);
dfs();dfs1();
scanf("%d",&m);
for(int i=;i<m;i++){
scanf("%d%d%d",&u,&v,&w);
if(w<=size*)printf("%d\n",query(u,v,w));
else printf("%d\n",query1(u,v,w));
}
return ;
}
CodeChef TRIPS-Children Trips 树上分块的更多相关文章
- Codechef TRIPS Children Trips (分块、倍增)
题目链接: https://www.codechef.com/problems/TRIPS 感觉CC有点毒瘤啊.. 题解: 首先有一个性质可能是因为太傻所以网上没人解释,然而我看了半天: 就是正序和倒 ...
- 【codechef】Children Trips
Portal -->CC_Children Trips Solution (英文题解看得真爽qwq不过写的好详细啊ovo) 首先这题有一个很重要的条件就是边权是\(1\)或者\(2\),所以虽然 ...
- 【CODECHEF】Children Trips 倍增
此题绝了,$O(n^{1.5}\ log\ n)$都可以过掉.... 题目大意:给你一颗$n$个点的树,每条边边权不是2就是$1$,有$m$个询问,每次询问一个人从$x$点走到$y$点,每天可以走的里 ...
- [CC-TRIPS]Children Trips
[CC-TRIPS]Children Trips 题目大意: \(n(n\le10^5)\)座城市构成一棵树,且树上的每条边的长度\(l_i\)满足\(1\le l_i\le 2\).\(m(m\le ...
- 洛谷P2325王室联邦 SCOI2005 构造+树上分块
正解:构造 解题报告: 照例先放传送门 umm其实我jio得这题应该在教树上莫队的时候港,应该是用来帮助理解树上莫队的分块方式的 然而这题是在学了树上分块之后再遇到的?就显得没那么难了吼 然后就随便说 ...
- [bzoj 3720] Gty的妹子树 (树上分块)
树上分块(块状树) Description 我曾在弦歌之中听过你, 檀板声碎,半出折子戏. 舞榭歌台被风吹去, 岁月深处尚有余音一缕-- Gty神(xian)犇(chong)从来不缺妹子-- 他来到了 ...
- CODECHEF Oct. Challenge 2014 Children Trips
@(XSY)[分塊, 倍增] Description There's a new trend among Bytelandian schools. The "Byteland Tourist ...
- 题解 Children Trips
题目传送门 Description 给出一个大小为 \(n\) 的边权全为 \(1,2\) 的带权树,有 \(q\) 此查询,每次给出 \(u,v,p\) ,问 \(u\to v\) 每次可以最多走边 ...
- BZOJ 1086: [SCOI2005]王室联邦 [树上分块]
portal 题意: 树分成若干块大小在$[s,3s]$之间,每块有一个根(可以不在块内),所有点到根路径上的点都必须在块内 据说这是一个保证了块大小直径个数的科学分块方法,貌似只有本题有用 我错了 ...
随机推荐
- AndroidStudio 搜索导入自己需要的库
前言 在Androidx的库应用后,导致有需多系统组件库需要重新 implementation 升级为Androidx, 但是你可能会发现不知道怎么导入他们. 当然除了导入Android的组件库,还 ...
- jQuery实现的文字逐行向上间歇滚动效果示例
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- scala中ArrayBuffer简单使用
import scala.collection.mutable.ArrayBuffer /** * 与Array区别: * 1.Array是不可变的,不能直接地对其元素进行删除操作,只能通过重赋值或过 ...
- prometheus配置详情
https://prometheus.io/docs/prometheus/latest/configuration/configuration/ 下面监控宿主机和容器的内存,CPU,磁盘等状态 gr ...
- python模块typing的作用
一.介绍 Python是一门弱类型的语言,很多时候我们可能不清楚函数参数类型或者返回值类型,很有可能导致一些类型没有指定方法,在写完代码一段时间后回过头看代码,很可能忘记了自己写的函数需要传什么参数, ...
- JAVA 设计的七大原则
一.开闭原则 开闭原则(Open-Closed Principle, OCP)是指一个软件实体如类.模块和函数应该对 扩展开放,对修改关闭. 所谓的开闭,也正是对扩展和修改两个行为的一个原则.强调 的 ...
- Spring源码由浅入深系列一 简介
概述: Spring是一个企业级的开源框架.它提供轻量级的依赖注入.面向切面编程.全方位的整合框架.下图是Spring框架的组成部分,各部分内容作了简单说明. 依赖注入: 依赖注入是S ...
- HYNB Round 8: 2016 ICPC Amritapuri Regionals
HYNB Round 8: 2016 ICPC Amritapuri Regionals A - Tim and BSTs 做法 经典的树 DP 问题. \(dp[u][i]\) 表示考虑以 u 为根 ...
- js input框限制输入为数字并限制长度
<input type="number" name="price" id="priceVal" placeholder="请 ...
- Ubuntu环境下使用npm编译从git上clone下来的前端(Javascript)项目
一.更新Ubuntu软件源 打开终端依次输入: $ sudo apt-get update $ sudo apt-get install -y python-software-properties s ...