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]$之间,每块有一个根(可以不在块内),所有点到根路径上的点都必须在块内 据说这是一个保证了块大小直径个数的科学分块方法,貌似只有本题有用 我错了 ...
随机推荐
- Android开发 自定义View_白色圆型涟漪动画View
代码: import android.animation.ValueAnimator; import android.content.Context; import android.graphics. ...
- js里json和eval()
JSON * - JS中的对象只有JS自己认识,其他的语言都不认识 * - JSON就是一个特殊格式的字符串,这个字符串可以被任意的语言所识别, * 并且可以转换为任意语言中的对象,JSON在开发中主 ...
- leetcode-210-课程表②
题目描述: 第一次提交: class Solution: def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -& ...
- python封装email模块
一.代码 from email.mime.text import MIMEText from email.header import Header from email.utils import pa ...
- C#字符串、字节数组和内存流间的相互转换 - IT浪潮之巅
定义string变量为str,内存流变量为ms,比特数组为bt 1.字符串=>比特数组 (1)byte[] bt=System.Text.Encoding.Default.GetBytes(&q ...
- wrapClass
参考文章 https://blog.csdn.net/wyk304443164/article/details/68484305
- 图解 5 种 Join 连接及实战案例!(inner/ left/ right/ full/ cross)
Join 连接在日常开发用得比较多,但大家都搞清楚了它们的使用区别吗??一文带你上车~~ 内连接 inner join 内连接是基于连接谓词将俩张表(如A和B)的列组合到一起产生新的结果表,在表中存在 ...
- 初识OpenCV-Python - 004: Trackbar as the color palette
此次学习了如何用OpenCV建立一个色调盘.其中会用到cv2.getTrackbarPos(), cv2.createTrackbar()函数. code: import cv2import nump ...
- 【AT3611】Tree MST
题目 这个题的输入首先就是一棵树,我们考虑一下点分 我们对于每一个分治重心考虑一下跨过这个分治重心的连边情况 就是把当前分治区域内所有的点向距离分治重心最近的点连边 考虑一下这个算法的正确性,如果我们 ...
- POJ 1873 /// 状压+凸包
题目大意: 国王有一片森林,巫师需要从所有树中选出一些做成围栏把其他树围起来, 每棵树都有其对应的价值 v 和能作为围栏的长度 l 要求最小价值,若存在多种最小价值的方案则选择余下长度更少的 树木较少 ...