题目大意

  http://uoj.ac/problem/295

题解

  考虑像 zkw 线段树一样,从 \([l-1,l-1],[r+1,r+1]\) 这两个区间开始往上跳,直到两个指针碰到一起为止。

  先求出每个点往上跳直到这个点是父亲的左儿子的点,再求出每个点往上跳直到这个点是父亲的右儿子的点。

  这样就可以快速求出要求的区间的信息了。

  然后对这几个东西搞个倍增或树剖就可以快速定位到一个点了。

  把 \(dist(x,y)\) 拆成 \(dist(x,y)=d_x+d_y-2d_{lca(x,y)}\),对三部分分开求。

  前两部分很好求,第三部分就分类讨论一下,在链上跳就好了。

  时间复杂度:\(O(n+m\log n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<functional>
#include<cmath>
#include<vector>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
using std::vector;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const int N=200010;
int ls[2*N],rs[2*N];
int cnt;
int rt;
int pos;
int c[2*N],e[2*N];
int a[2*N];
int st[2*N];
int ed[2*N];
int ti;
int d[2*N];
int f[2*N];
int lf[2*N];
int rf[2*N];
int lv[2*N];
ll ld[2*N];
int rv[2*N];
ll rd[2*N];
int f1[2*N][20];
int f2[2*N][20];
int f3[2*N][20];
void build(int &p,int l,int r,int fa,int dep)
{
p=++cnt;
st[p]=++ti;
d[p]=dep;
f[p]=fa;
if(p==ls[f[p]])
{
lf[p]=f[p];
rf[p]=rf[f[p]];
}
else
{
lf[p]=lf[f[p]];
rf[p]=f[p];
} if(l==r)
{
lv[p]=lv[lf[p]];
ld[p]=ld[lf[p]];
rv[p]=rv[rf[p]];
rd[p]=rd[rf[p]];
a[l]=p;
ed[p]=ti;
return;
} lv[p]=1;
ld[p]=dep+1;
rv[p]=1;
rd[p]=dep+1; lv[p]+=lv[lf[p]];
ld[p]+=ld[lf[p]];
rv[p]+=rv[rf[p]];
rd[p]+=rd[rf[p]]; e[p]=c[++pos];
build(ls[p],l,e[p],p,dep+1);
build(rs[p],e[p]+1,r,p,dep+1);
ed[p]=ti;
}
int n,m;
void init()
{
for(int i=1;i<=cnt;i++)
{
f1[i][0]=f[i];
for(int j=1;j<=19;j++)
f1[i][j]=f1[f1[i][j-1]][j-1];
f2[i][0]=lf[i];
for(int j=1;j<=19;j++)
f2[i][j]=f2[f2[i][j-1]][j-1];
f3[i][0]=rf[i];
for(int j=1;j<=19;j++)
f3[i][j]=f3[f3[i][j-1]][j-1];
}
}
int getlca(int x,int y)
{
if(d[x]<d[y])
swap(x,y);
for(int i=19;i>=0;i--)
if(d[f1[x][i]]>=d[y])
x=f1[x][i];
if(x==y)
return x;
for(int i=19;i>=0;i--)
if(f1[x][i]!=f1[y][i])
{
x=f1[x][i];
y=f1[y][i];
}
return f1[x][0];
while(x!=y)
if(d[x]>d[y])
x=f[x];
else
y=f[y];
return x;
}
int is_ancestor(int x,int y)
{
return st[x]<=st[y]&&ed[x]>=ed[y];
}
ll query_l_chain(int x,int u)
{
if(!ls[x])
x=lf[x];
ll res=(ll)lv[x]*d[u]+ld[x];
int lca=getlca(x,u);
for(int i=19;i>=0;i--)
if(d[f2[x][i]]>d[lca])
{
res-=d[lca]<<(i+1);
x=f2[x][i];
}
// res-=2*d[lca];
// x-=f2[x][0];
while(d[x]>d[lca])
{
res-=2*d[lca];
x=lf[x];
}
if(!x)
return res;
if(is_ancestor(rs[x],u))
res-=2*(d[lca]+1);
else
res-=2*d[x];
res-=2*(ld[lf[x]]-lv[lf[x]]);
return res;
}
ll query_r_chain(int x,int u)
{
if(!rs[x])
x=rf[x];
ll res=(ll)rv[x]*d[u]+rd[x];
int lca=getlca(x,u);
for(int i=19;i>=0;i--)
if(d[f3[x][i]]>d[lca])
{
res-=d[lca]<<(i+1);
x=f3[x][i];
}
// res-=2*d[lca];
// x-=f3[x][0];
while(d[x]>d[lca])
{
res-=2*d[lca];
x=rf[x];
}
if(!x)
return res;
if(is_ancestor(ls[x],u))
res-=2*(d[lca]+1);
else
res-=2*d[x];
res-=2*(rd[rf[x]]-rv[rf[x]]);
return res;
}
ll query(int u,int l,int r)
{
l--;
r++;
int x=a[l];
int y=a[r];
int lca=getlca(x,y);
ll s1=query_l_chain(x,u);
ll s2=query_l_chain(lca,u);
ll s3=query_r_chain(y,u);
ll s4=query_r_chain(lca,u);
return s1-s2+s3-s4;
}
int main()
{
open("loj2570");
scanf("%d",&n);
c[1]=0;
c[2]=n;
for(int i=3;i<=n+1;i++)
scanf("%d",&c[i]);
build(rt,0,n+1,0,1);
init();
scanf("%d",&m);
int u,l,r;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&l,&r);
u+=3;
printf("%lld\n",query(u,l,r));
}
return 0;
}

【UOJ295】【ZJOI2017】线段树 倍增的更多相关文章

  1. ZJOI2017线段树

    ZJOI2017线段树 题意: ​ 给你一颗广义线段树,太长了,自己去看. 题解: ​ 直接上zkw那一套,把闭区间换成开区间,就是把取\([l,r]\),变成取\([l-1,l-1],[r+1,r+ ...

  2. isaster(Comet OJ - Contest #11D题+kruskal重构树+线段树+倍增)

    目录 题目链接 思路 代码 题目链接 传送门 思路 \(kruskal\)重构树\(+\)线段树\(+\)倍增 代码 #include <set> #include <map> ...

  3. 2019.03.26 bzoj4444: [Scoi2015]国旗计划(线段树+倍增)

    传送门 题意简述:现在给你一个长度为mmm的环,有nnn条互不包含的线段,问如果强制选第iii条线段至少需要用几条线段覆盖这个环,注意用来的覆盖的线段应该相交,即[1,3],[4,5][1,3],[4 ...

  4. 【BZOJ 3133】 3133: [Baltic2013]ballmachine (线段树+倍增)

    3133: [Baltic2013]ballmachine Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 148  Solved: 66 Descri ...

  5. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  6. 树上第k小,可持久化线段树+倍增lca

    给定一颗树,树的每个结点都有权值, 有q个询问,每个询问是 u v k ,表示u到v路径上第k小的权值是多少. 每个结点所表示的线段树,是父亲结点的线段树添加该结点的权值之后形成的新的线段树 c[ro ...

  7. 2018.11.01 NOIP训练 图论(线段树+倍增+dfs序)

    传送门 一道挺妙的题. 对于询问点(u,v),如右图所示,我们可以发现存在一个点m在u->v的路径中,m子树的点到u是最近的,m子树外到v是最近的.其中dis(u,m)=(dis(u,v)-1) ...

  8. [BZOJ4876][ZJOI2017]线段树

    没有用到任何算法,代码只有60+行,但是细节多如牛毛,各种分类讨论必须全部想清楚才行. https://www.cnblogs.com/xiejiadong/p/6811289.html #inclu ...

  9. bzoj 3779: 重组病毒 LCT+线段树+倍增

    题目: 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒.这种病毒的繁殖和变异能力极强.为了阻止这种病毒传播,某安全机构策划了一次实验,来研究这种病毒. 实验在一个封闭 ...

随机推荐

  1. TS学习随笔(四)->数组的类型

    少侠们,今天我们继续来搞一搞TS 今天我们要来看一看TS中数组的定义是个什么鬼样子 数组的类型: 在 TypeScript 中,数组类型有多种定义方式,比较灵活.下面我们来看看有哪些定义方法 「类型 ...

  2. JMeter 内置日期(时间)函数总结

    JMeter 内置日期(时间)函数总结   by:授客 QQ:1033553122 1. 测试环境 apache-jmeter-3.3 下载地址: http://jmeter.apache.org/c ...

  3. ionic3 Loading组件的用法

    import { LoadingController } from 'ionic-angular'; @Component({ selector: 'page-contact', templateUr ...

  4. Centos7 安装Tomcat并运行程序

    运行环境:Centos7 jdk:1.8.0_171  Tocmcat:8.5.31 下载地址 :https://tomcat.apache.org/download-80.cgi#8.5.31 Ap ...

  5. Spring MVC中用@ResponseBody转json,对json进行处理方法汇总

    <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> < ...

  6. 如何利用MongoDB打造TOP榜小程序

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯云数据库 TencentDB发表于云+社区专栏 今天我分享的主题内容大概是两部分,最主要的还是小游戏和小程序,第一部分就是跟大家分 ...

  7. windows批处理添加AD域账户

    因为要用个批处理命令在Windows Server里面批量添加域用户,所以需要使用批处理命令. 我这篇是纯新手教程,在百度上搜了一些批处理命令感觉属于进阶教程,研究了两天才完成我要完成的目标. 下面从 ...

  8. CentOS7 Docker私有仓库搭建及删除镜像 【转】

    文章来源:centos7 Docker私有仓库搭建及删除镜像 如果不想用私有镜像库,你可以用docker的库 https://hub.docker.com 环境准备 环境:两个装有Docker 17. ...

  9. luffy项目后台drf搭建(1)

    一 进入虚拟环境 打开crm,输入命令 workon luffy 虚拟环境使用文档 二 安装基本类库 pip install django pip install PymySQL pip instal ...

  10. Spring事件和监听器

    Application下抽象子类ApplicationContextEvent的下面有4个已经实现好的事件 ContextClosedEvent(容器关闭时) ContextRefreshedEven ...