【UOJ295】【ZJOI2017】线段树 倍增
题目大意
题解
考虑像 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】线段树 倍增的更多相关文章
- ZJOI2017线段树
ZJOI2017线段树 题意: 给你一颗广义线段树,太长了,自己去看. 题解: 直接上zkw那一套,把闭区间换成开区间,就是把取\([l,r]\),变成取\([l-1,l-1],[r+1,r+ ...
- isaster(Comet OJ - Contest #11D题+kruskal重构树+线段树+倍增)
目录 题目链接 思路 代码 题目链接 传送门 思路 \(kruskal\)重构树\(+\)线段树\(+\)倍增 代码 #include <set> #include <map> ...
- 2019.03.26 bzoj4444: [Scoi2015]国旗计划(线段树+倍增)
传送门 题意简述:现在给你一个长度为mmm的环,有nnn条互不包含的线段,问如果强制选第iii条线段至少需要用几条线段覆盖这个环,注意用来的覆盖的线段应该相交,即[1,3],[4,5][1,3],[4 ...
- 【BZOJ 3133】 3133: [Baltic2013]ballmachine (线段树+倍增)
3133: [Baltic2013]ballmachine Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 148 Solved: 66 Descri ...
- 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)
题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...
- 树上第k小,可持久化线段树+倍增lca
给定一颗树,树的每个结点都有权值, 有q个询问,每个询问是 u v k ,表示u到v路径上第k小的权值是多少. 每个结点所表示的线段树,是父亲结点的线段树添加该结点的权值之后形成的新的线段树 c[ro ...
- 2018.11.01 NOIP训练 图论(线段树+倍增+dfs序)
传送门 一道挺妙的题. 对于询问点(u,v),如右图所示,我们可以发现存在一个点m在u->v的路径中,m子树的点到u是最近的,m子树外到v是最近的.其中dis(u,m)=(dis(u,v)-1) ...
- [BZOJ4876][ZJOI2017]线段树
没有用到任何算法,代码只有60+行,但是细节多如牛毛,各种分类讨论必须全部想清楚才行. https://www.cnblogs.com/xiejiadong/p/6811289.html #inclu ...
- bzoj 3779: 重组病毒 LCT+线段树+倍增
题目: 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒.这种病毒的繁殖和变异能力极强.为了阻止这种病毒传播,某安全机构策划了一次实验,来研究这种病毒. 实验在一个封闭 ...
随机推荐
- TS学习随笔(四)->数组的类型
少侠们,今天我们继续来搞一搞TS 今天我们要来看一看TS中数组的定义是个什么鬼样子 数组的类型: 在 TypeScript 中,数组类型有多种定义方式,比较灵活.下面我们来看看有哪些定义方法 「类型 ...
- SAP MM ME1M报表结果不科学?
SAP MM ME1M报表结果不科学? 做过SAP MM顾问的都知道,报表ME1M可以查询物料的info record列表,即是说可以以列表的形式批量显示多个物料的采购价格主数据. 但是这个报表有个不 ...
- 小记 xian80 坐标转换 wgs84
转坐标这个问题是个老生常谈的话题了. 昨天遇到同事求助将 xian80的平面坐标转换到2000下. 想了一下,因为暂时还没有现成的2000的dwg数据可用,只能暂时以wgs84的为准了,然而有个问题, ...
- java 线程方法 ---- join()
class MyThread2 implements Runnable{ @Override public void run() { for (int i = 0; i < 5; i++){ S ...
- 【已解决】checkout 配置无效的问题可以进来看下
在日常工作中,我们经常会遇到要更新一个项目,但是由于更改了配置,需要将这些配置commit或者checkout,但是有的同学不想commit怎么办呢,只能通过checkout,那么问题又来了,改了很多 ...
- java 中文乱码以及转码
查看此文章需要对字符集编码有一定的认识:任意门:字符集编码基础 一.字符串的内部表示? 重点:字符串在java(指在JVM中.在内存中)中统一用unicode表示( 即utf-16 LE) , 下面解 ...
- C++17剖析:string在Modern C++中的实现
概述 GCC 8.2提供了两个版本的std::string:一个是基于Copy On Write的,另一个直接字符串拷贝的.前者针对C++11以前的,那时候没有移动构造,一切以效率为先,需要使用COW ...
- 【原】Java学习笔记013 - 阶段测试
package cn.temptation; import java.util.Scanner; public class Sample01 { public static void main(Str ...
- c/c++ 多线程 层级锁
多线程 层级锁 当要同时操作2个对象时,就需要同时锁定这2个对象,而不是先锁定一个,然后再锁定另一个.同时锁定多个对象的方法:std::lock(对象1.锁,对象2.锁...) 但是,有的时候,并不能 ...
- python--多继承
多继承 子类可以拥有多个父类,继承所有父类的属性和方法 class 子类名(父类名1,父类名2): 多个父类直接不要有重名的方法和属性,子类对象调用,没法确认.