题目大意

  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. iOS----------对单元格取余

    if (indexPath.row % 2 == 0) { cell.backgroundColor = [UIColor magentaColor]; }else{ cell.backgroundC ...

  2. Bitmap上下合成图片

    合成两张图片,上下叠加的效果: /** * 把两个位图覆盖合成为一个位图,以底层位图的长宽为基准 * * @param backBitmap 在底部的位图 * @param frontBitmap 盖 ...

  3. 最简单打开三星note8三星galaxy susb调试模式的方法

    每当我们使用安卓手机连接PC的时候,如果手机没有开启usb调试模式,PC则无办法成功检测到我们的手机,部分APP也无办法正常使用,这时我们需要找处理方法将手机的usb调试模式开启,以下内容我们介绍三星 ...

  4. 安卓开发:UI组件-图片控件ImageView(使用Glide)和ScrollView

    2.7ImageView 2.7.1插入本地图片 一个图片控件,可以用来显示本地和网络图片. 在首页添加按钮ImageView,指向新页面(步骤与前同,不再详写). activity_image_vi ...

  5. JavaScript中闭包的写法和作用详解

    1.什么是闭包 闭包是有权访问另一个函数作用域的变量的函数. 简单的说,Javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内.而且,这些内部函数可以访问它们所在的外 ...

  6. Oracle获取表字段名,字段类型,字段长度,注释

    SELECT b.comments as 注释, a.column_name as 列名, a.data_type || '(' || a.data_length || ')' as 数据类型, a. ...

  7. js调用浏览器打印指定div内容

    --打印按钮事件 function printForm(){    var headstr = '<html xmlns:th="http://www.thymeleaf.org&qu ...

  8. c/c++ 网络编程 UDP 改变网关和网卡名字

    网络编程 UDP 改变网关和网卡名字 在程序里动态改变网关和网卡名字 1,改变网卡名字 #include <stdio.h> #include <string.h> #incl ...

  9. webstorm 的 .后缀名-tab快捷键

    if (key) {}//key.if tab if (!key) {}//key.else tab if (key != null) {}//key.notnull tab if (typeof k ...

  10. day 9~11 函数

    今日内容 '''函数四个组成部分函数名:保存的是函数的地址,是调用函数的依据函数体:就是执行特定功能的代码块函数返回值:代码块执行的结果反馈函数参数:完成功能需要的条件信息​1.函数的概念2.函数的定 ...