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*2];
int n,size,fi[maxn],se,stack[maxn],top,depth[maxn],fa[maxn],block[maxn],s,remain[maxn][maxs*2],step[maxn][maxs*2],key[maxs],dis[maxn];
int reach[maxn][maxs*2],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=1,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]+1,si+=dfs(v);
    }
    if(depth[x]%size==0&&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][0]=x;
    reach[x][1]=dis[x]==1?fa[x]:x;
    for(int i=2;i<=size*2;i++){
        if(i-dis[x]>=1)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=2;i<=size*2;i++){
            if(block[x]==block[v=reach[x][i]]){
                step[x][i]=step[v][i]+1;
                remain[x][i]=remain[v][i];
            }
            else{
                step[x][i]=1;remain[x][i]=remain[fa[x]][i]-dis[x];
            }
        }
        dis1[x]=dis1[fa[x]]+dis[x];
    }
    else{
        for(int i=2;i<=size*2;i++){
            step[x][i]=1;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=0,sup1=0,sup2=0;
    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=0,sup1=0,sup2=0;
    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<<1)){
                    if(reach[l][size<<1]==reach[l][(size<<1)-1])sup1-=(size<<1)-1;
                    else sup1-=(size<<1);
                    l=reach[l][size<<1];
                }
                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<<1)){
                    if(reach[r][size<<1]==reach[r][(size<<1)-1])sup2-=(size<<1)-1;
                    else sup2-=(size<<1);
                    r=reach[r][size<<1];
                }
                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=1;i<n;i++)scanf("%d%d%d",&u,&v,&w),add_edge(u,v,w);
    dfs(1);dfs1(1);
    scanf("%d",&m);
    for(int i=0;i<m;i++){
        scanf("%d%d%d",&u,&v,&w);
        if(w<=size*2)printf("%d\n",query(u,v,w));
        else printf("%d\n",query1(u,v,w));
    }
    return 0;
}
posted @ 2018-03-09 14:11  Bennettz  阅读(382)  评论(1编辑  收藏  举报