当前位置: 首页 > news >正文

Loj #2570. 「ZJOI2017」线段树

Loj #2570. 「ZJOI2017」线段树

题目描述

线段树是九条可怜很喜欢的一个数据结构,它拥有着简单的结构、优秀的复杂度与强大的功能,因此可怜曾经花了很长时间研究线段树的一些性质。

最近可怜又开始研究起线段树来了,有所不同的是,她把目光放在了更广义的线段树上:在正常的线段树中,对于区间 \([l, r]\),我们会取 \(m = \lfloor \frac{l+r}{2} \rfloor\),然后将这个区间分成 \([l, m]\)\([m + 1, r]\) 两个子区间。在广义的线段树中,\(m\) 不要求恰好等于区间的中点,但是 \(m\) 还是必

须满足 \(l \le m < r\) 的。不难发现在广义的线段树中,树的深度可以达到 \(O(n)\) 级别。

例如下面这棵树,就是一棵广义的线段树:

Segment tree

为了方便,我们按照先序遍历给线段树上所有的节点标号,例如在上图中,\([2, 3]\) 的标号是 \(5\)\([4, 4]\) 的标号是 \(9\),不难发现在 \([1, n]\) 上建立的广义线段树,它共有着 \(2n − 1\) 个节点。

考虑把线段树上的定位区间操作 \((\)就是打懒标记的时候干的事情\()\) 移植到广义线段树上,可以发现在广义的线段树上还是可以用传统的线段树上的方法定位区间的,例如在上图中,蓝色节点和蓝色边就是在定位区间 \([2, 4]\) 时经过的点和边,最终定位到的点是 \([2, 3]\)\([4, 4]\)

输入格式

第一行输入一个整数 \(n\)

接下来一行包含 \(n - 1\) 个空格隔开的整数:按照标号递增的顺序,给出广义线段树上所有非叶子 节点的划分位置 \(m\)。不难发现通过这些信息就能唯一确定一棵 \([1, n]\) 上的广义线段树。

接下来一行输入一个整数 \(m\)

之后 \(m\) 行每行输入三个整数 \(u, l, r\ (1 \le u \le 2n − 1, 1 \le l \le r \le n)\),表示一组询问。

输出格式

对于每组询问,输出一个整数表示答案。

数据范围与提示

对于 \(100\%\) 的数据,保证 \(2\leq n\leq 10^5, m\leq 10^5\)

首先线段树上询问\([l,r]\)所访问到的节点就是\(l-1\)所代表的的节点往上走,访问所以经过节点的右儿子(如果有的话);以及\(r+1\)所代表的节点往上走访问的左儿子。直到两个点走到\(lca\)处停止(\(lca\)处不访问)。

这样我们就可以用倍增,来计算某个点到其某个祖先路径上所有的 左/右 儿子的 个数/到根距离和。问题是怎么求这些点到给定点\(u\)\(lca\)

我们假设\(l-1\)所代表的节点为\(v\)(处理右边的同理)。我们先求出\(u\)\(v\)\(lca\)记为\(LCA\)。然后我们分两段统计右儿子\((v,LCA]\)\((LCA,f)\)。前一段的右儿子与\(u\)\(lca\)就是\(LCA\),后一段的\(lca\)就是每个右儿子的父亲。当然如果\(LCA\)\(f\)的祖先那么只统计\((v,f)\)

然后有个坑点,比如说某个右儿子\(son\)\(LCA\)\(son\)的祖先,\(son\)\(u\)的祖先(可以发现,最多只有一个这样的\(son\)),那么\(u\)\(son\)的距离被多算了\(2\),减去就好了。

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 400005

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

int n,m;
struct road {int to,nxt;}s[N<<1];
int h[N],cnt;
void add(int i,int j) {s[++cnt]=(road) {j,h[i]};h[i]=cnt;}
int Div[N];
int tot,div_tim;

int ls[N],rs[N];
int L[N],R[N],Mid[N];
int pos[N],dep[N];
int fa[N][20];
int lsum[N][20],rsum[N][20];
int lsize[N][20],rsize[N][20];

void dfs(int &v,int l,int r,int f) {
    v=++tot;
    fa[v][0]=f;
    dep[v]=dep[f]+1;
    for(int i=1;i<=18;i++) fa[v][i]=fa[fa[v][i-1]][i-1];
    L[v]=l,R[v]=r;
    if(l==r) {
        pos[l]=v;
        return ;
    }
    int mid=Div[++div_tim];
    Mid[v]=mid;
    dfs(ls[v],l,mid,v),dfs(rs[v],mid+1,r,v);
}

int lca(int a,int b) {
    if(dep[a]<dep[b]) swap(a,b);
    for(int i=18;i>=0;i--)
        if(fa[a][i]&&dep[fa[a][i]]>=dep[b])
            a=fa[a][i];
    if(a==b) return a;
    for(int i=18;i>=0;i--)
        if(fa[a][i]!=fa[b][i])
            a=fa[a][i],b=fa[b][i];
    return fa[a][0];
}

int Get_dis(int a,int b) {return dep[a]+dep[b]-2*dep[lca(a,b)];}

int Find_below(int v,int f) {
    for(int i=18;i>=0;i--)
        if(fa[v][i]&&dep[fa[v][i]]>dep[f])
            v=fa[v][i];
    return v;
}

void Findl(int v,int f,ll &size,ll &sum) {
    for(int i=18;i>=0;i--) {
        if(fa[v][i]&&dep[fa[v][i]]>=dep[f]) {
            size+=lsize[v][i];
            sum+=lsum[v][i];
            v=fa[v][i];
        }
    }
}

void Findr(int v,int f,ll &size,ll &sum) {
    for(int i=18;i>=0;i--) {
        if(fa[v][i]&&dep[fa[v][i]]>=dep[f]) {
            size+=rsize[v][i];
            sum+=rsum[v][i];
            v=fa[v][i];
        }
    }
}

ll cal_l(int v,int u,int f) {
    int Lca=lca(v,u);
    ll ans=0;
    ll belz=0,bels=0;
    ll upz=0,ups=0;
    Findr(v,dep[f]>dep[Lca]?f:Lca,belz,bels);
    if(dep[Lca]>dep[f]) {
        Findr(Lca,f,upz,ups);
    }
    ans=bels+belz*dep[u]-2*belz*dep[Lca]+ups+upz*dep[u]-2*(ups-upz);
    if(Find_below(u,Lca)==rs[Lca]&&dep[Lca]>=dep[f]) ans-=2;
    return ans;
}

ll cal_r(int v,int u,int f) {
    int Lca=lca(v,u);
    ll ans=0;
    ll belz=0,bels=0;
    ll upz=0,ups=0;
    Findl(v,dep[f]>dep[Lca]?f:Lca,belz,bels);
    if(dep[Lca]>dep[f]) {
        Findl(Lca,f,upz,ups);
    }
    ans=bels+belz*dep[u]-2*belz*dep[Lca]+ups+upz*dep[u]-2*(ups-upz);
    if(Find_below(u,Lca)==ls[Lca]&&dep[Lca]>=dep[f]) ans-=2;
    return ans;
}

void solve(int u,int l,int r) {
    ll ans=0;
    if(l==1&&r==n) {
        cout<<dep[u]-1<<"\n";
    } else {
        int LCA;
        if(l==1||r==n) LCA=1;
        else LCA=lca(pos[l-1],pos[r+1]);
        if(l==1&&r>=Mid[1]) ans+=Get_dis(u,ls[1]);
        if(r==n&&l<=Mid[1]+1) ans+=Get_dis(u,rs[1]);
        if(l!=1) ans+=cal_l(pos[l-1],u,Find_below(pos[l-1],LCA));
        if(r!=n) ans+=cal_r(pos[r+1],u,Find_below(pos[r+1],LCA));
        cout<<ans<<"\n";
    }
}

int main() {
    n=Get();
    for(int i=1;i<n;i++) Div[i]=Get();
    int rt;
    dep[1]=1;
    dfs(rt,1,n,0);

    for(int i=2;i<=tot;i++) {
        if(i==ls[fa[i][0]]) {
            rsum[i][0]=dep[rs[fa[i][0]]],rsize[i][0]=1;
        } else {
            lsum[i][0]=dep[ls[fa[i][0]]],lsize[i][0]=1;
        }
    }
    
    for(int j=1;j<=18;j++) {
        for(int i=1;i<=tot;i++) {
            if(fa[i][j]) {
                lsum[i][j]=lsum[i][j-1]+lsum[fa[i][j-1]][j-1];
                lsize[i][j]=lsize[i][j-1]+lsize[fa[i][j-1]][j-1];
                rsum[i][j]=rsum[i][j-1]+rsum[fa[i][j-1]][j-1];
                rsize[i][j]=rsize[i][j-1]+rsize[fa[i][j-1]][j-1];
            }
        }
    }
    
    m=Get();
    int u,l,r;
    while(m--) {
        u=Get(),l=Get(),r=Get();
        solve(u,l,r);
    }
    return 0;
}

转载于:https://www.cnblogs.com/hchhch233/p/10800564.html

相关文章:

  • 我理解的CLH
  • Java并发——结合CountDownLatch源码、Semaphore源码及ReentrantLock源码来看AQS原理
  • 走进 JDK 之 LinkedList
  • Python Day19
  • random,json,pickle,hashlib,shutil,hmac,shelve 模块
  • 支付宝架构师眼中的高并发架构 阅读笔记
  • 【一起学爬虫】scrapy框架的安装
  • java 实现DFA 算法(理论百度搜索)
  • v-lazyload数据变化图片不切换
  • 记录微博爬虫遇到问题
  • 一张思维导图辅助你深入了解 Vue | Vue-Router | Vuex 源码架构
  • SpringBoot Cmd运行Jar文件指定active文件的命令如下
  • JavaScript短信验证码60秒倒计时插件
  • 雷林鹏分享:让nginx支持CodeIgniter框架
  • 看看这些大龄程序员都做了些什么
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • Android开源项目规范总结
  • centos安装java运行环境jdk+tomcat
  • classpath对获取配置文件的影响
  • CSS实用技巧
  • Docker: 容器互访的三种方式
  • Git初体验
  • JavaScript 无符号位移运算符 三个大于号 的使用方法
  • Laravel核心解读--Facades
  • Node 版本管理
  • Python 基础起步 (十) 什么叫函数?
  • Vim Clutch | 面向脚踏板编程……
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 简单数学运算程序(不定期更新)
  • 批量截取pdf文件
  • 前言-如何学习区块链
  • 区块链共识机制优缺点对比都是什么
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 译有关态射的一切
  • 优秀架构师必须掌握的架构思维
  • 责任链模式的两种实现
  • 白色的风信子
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • #我与Java虚拟机的故事#连载19:等我技术变强了,我会去看你的 ​
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (附源码)ssm高校社团管理系统 毕业设计 234162
  • (九)信息融合方式简介
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • (转)重识new
  • (转载)CentOS查看系统信息|CentOS查看命令
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .NET Remoting学习笔记(三)信道
  • .NET 材料检测系统崩溃分析
  • .net 发送邮件
  • .net 后台导出excel ,word
  • .net6+aspose.words导出word并转pdf
  • .NETCORE 开发登录接口MFA谷歌多因子身份验证