参考文献国家集训队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. Android开发 自定义View_白色圆型涟漪动画View

    代码: import android.animation.ValueAnimator; import android.content.Context; import android.graphics. ...

  2. js里json和eval()

    JSON * - JS中的对象只有JS自己认识,其他的语言都不认识 * - JSON就是一个特殊格式的字符串,这个字符串可以被任意的语言所识别, * 并且可以转换为任意语言中的对象,JSON在开发中主 ...

  3. leetcode-210-课程表②

    题目描述: 第一次提交: class Solution: def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -& ...

  4. python封装email模块

    一.代码 from email.mime.text import MIMEText from email.header import Header from email.utils import pa ...

  5. C#字符串、字节数组和内存流间的相互转换 - IT浪潮之巅

    定义string变量为str,内存流变量为ms,比特数组为bt 1.字符串=>比特数组 (1)byte[] bt=System.Text.Encoding.Default.GetBytes(&q ...

  6. wrapClass

    参考文章   https://blog.csdn.net/wyk304443164/article/details/68484305

  7. 图解 5 种 Join 连接及实战案例!(inner/ left/ right/ full/ cross)

    Join 连接在日常开发用得比较多,但大家都搞清楚了它们的使用区别吗??一文带你上车~~ 内连接 inner join 内连接是基于连接谓词将俩张表(如A和B)的列组合到一起产生新的结果表,在表中存在 ...

  8. 初识OpenCV-Python - 004: Trackbar as the color palette

    此次学习了如何用OpenCV建立一个色调盘.其中会用到cv2.getTrackbarPos(), cv2.createTrackbar()函数. code: import cv2import nump ...

  9. 【AT3611】Tree MST

    题目 这个题的输入首先就是一棵树,我们考虑一下点分 我们对于每一个分治重心考虑一下跨过这个分治重心的连边情况 就是把当前分治区域内所有的点向距离分治重心最近的点连边 考虑一下这个算法的正确性,如果我们 ...

  10. POJ 1873 /// 状压+凸包

    题目大意: 国王有一片森林,巫师需要从所有树中选出一些做成围栏把其他树围起来, 每棵树都有其对应的价值 v 和能作为围栏的长度 l 要求最小价值,若存在多种最小价值的方案则选择余下长度更少的 树木较少 ...