参考文献国家集训队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 树上分块的更多相关文章

  1. Codechef TRIPS Children Trips (分块、倍增)

    题目链接: https://www.codechef.com/problems/TRIPS 感觉CC有点毒瘤啊.. 题解: 首先有一个性质可能是因为太傻所以网上没人解释,然而我看了半天: 就是正序和倒 ...

  2. 【codechef】Children Trips

    Portal -->CC_Children Trips Solution (英文题解看得真爽qwq不过写的好详细啊ovo) 首先这题有一个很重要的条件就是边权是\(1\)或者\(2\),所以虽然 ...

  3. 【CODECHEF】Children Trips 倍增

    此题绝了,$O(n^{1.5}\ log\ n)$都可以过掉.... 题目大意:给你一颗$n$个点的树,每条边边权不是2就是$1$,有$m$个询问,每次询问一个人从$x$点走到$y$点,每天可以走的里 ...

  4. [CC-TRIPS]Children Trips

    [CC-TRIPS]Children Trips 题目大意: \(n(n\le10^5)\)座城市构成一棵树,且树上的每条边的长度\(l_i\)满足\(1\le l_i\le 2\).\(m(m\le ...

  5. 洛谷P2325王室联邦 SCOI2005 构造+树上分块

    正解:构造 解题报告: 照例先放传送门 umm其实我jio得这题应该在教树上莫队的时候港,应该是用来帮助理解树上莫队的分块方式的 然而这题是在学了树上分块之后再遇到的?就显得没那么难了吼 然后就随便说 ...

  6. [bzoj 3720] Gty的妹子树 (树上分块)

    树上分块(块状树) Description 我曾在弦歌之中听过你, 檀板声碎,半出折子戏. 舞榭歌台被风吹去, 岁月深处尚有余音一缕-- Gty神(xian)犇(chong)从来不缺妹子-- 他来到了 ...

  7. CODECHEF Oct. Challenge 2014 Children Trips

    @(XSY)[分塊, 倍增] Description There's a new trend among Bytelandian schools. The "Byteland Tourist ...

  8. 题解 Children Trips

    题目传送门 Description 给出一个大小为 \(n\) 的边权全为 \(1,2\) 的带权树,有 \(q\) 此查询,每次给出 \(u,v,p\) ,问 \(u\to v\) 每次可以最多走边 ...

  9. BZOJ 1086: [SCOI2005]王室联邦 [树上分块]

    portal 题意: 树分成若干块大小在$[s,3s]$之间,每块有一个根(可以不在块内),所有点到根路径上的点都必须在块内 据说这是一个保证了块大小直径个数的科学分块方法,貌似只有本题有用  我错了 ...

随机推荐

  1. Unable to resolve dependency for ':app@debug/compileClasspath': Could not resolve com.android.support:appcompat-v7:26.1.0

    android studio  3.0 出现此问题可能是因为 你的android studio 时脱机状态 无法下载资源 这时候你可以点击左上角分File->Other Settings-> ...

  2. leetcode-219-存在重复元素②

    题目描述: 第一次提交:超时 class Solution: def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool ...

  3. 【转载】浅谈Linux内存管理机制

    经常遇到一些刚接触Linux的新手会问内存占用怎么那么多? 在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,在 ...

  4. Delphi定时模拟键盘按键例程

    delphi模拟键盘按键实例delphi模拟键盘按键实例,只是模拟一个按键的例子而已.到一定时间按下模拟按下一个按键,delphi7编译通过. 10秒点击一下H键,其他键你们去找数值替换吧,网上大把的 ...

  5. php算法题---连续子数组的最大和

    php算法题---连续子数组的最大和 一.总结 一句话总结: 重要:一定要本机调试过了再提交代码,因为很容易出现考虑不周的情况,或者修改了之后没有考虑修改的这部分 利用空间来换时间,或者利用空间来换算 ...

  6. hive 总结三(压缩)

    本文参考:黑泽君相关博客 本文是我总结日常工作中遇到的坑,结合黑泽君相关博客,选取.补充了部分内容. 开启 map 输出阶段压缩可以减少 job 中 map 和 Reduce task 间数据传输量. ...

  7. shell 一些题目

    在a.log中精确查找含有msyql单词的行a.log文件内容如下: mysqlmysql mysqlmysql aa mysql_mysqla mysql b_mysql aa _mysqla _m ...

  8. springboot启动器:spring-boot-starter

    今天想要导入thymeleaf的依赖,但是又不想从其他博复制粘贴,于是去spring官方文档找一找 在idea新建的springbootweb项目中,有一个HELP.md文件,里面包含spring w ...

  9. Python - Virtualenv 创建虚拟环境

    Virtualenv 回到顶部 为了解决各个项目的共同依赖同一个环境,造成版本冲突等,virtualenv创建一个干净的环境,在这个环境下,进行Python项目的开发等,就成为一个个独立的项目,从而避 ...

  10. <linux常用命令>初级版

    显示时间 date 显示日历cal 变换目录 cd 显示当前所在目录 pwd 建立新目录 mkdir -p a/b/c 删除空目录 rmdir 当前目录下文件和目录显示 ls 复制 cp 文件 路径 ...