先放代码,日后更。
============================2018.3.21UPD============================
题面在这里
做法
首先需要了解zkw线段树的操作过程。(不懂的百度一下)
大概就是从两个叶节点开始,维护两个指针,一个指向
l
l
左边一位,一个指向右边一位,不停向上跳。然后模仿这个过程同样在这个广义线段树上操作,画一下图可以发现,假设
l−1
l
−
1
所在叶节点
u
u
,所在叶节点
v
v
,一个线段会分成的若干个小线段恰好是
u−lca(u,v)
u
−
l
c
a
(
u
,
v
)
链上所有左儿子的右兄弟,以及
v−lca(u,v)
v
−
l
c
a
(
u
,
v
)
链上所有右儿子的左兄弟。
一定要画图!!!多观察!!!
发现这一点后就很容易了。将一条链按
lca
l
c
a
分成两半,然后大力分类讨论,树上倍增处理一下,细节超多。比如,
l=1
l
=
1
和
r=n
r
=
n
的情况需要特殊处理,还有
u
u
<script type="math/tex" id="MathJax-Element-156">u</script>的位置比较特殊的时候也需要判一下。
建议自己推推看qwq。(我当时都是自己想的qwq,感觉想出一道zjoi题自己萌!萌!哒!
代码
/*
* zkw线段树的科技;
* 一堆细节;
* 考试一定要对拍!不对拍会死的!
*/
#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x); i<=(y); i++)
#define per(i,x,y) for (int i=(x); i>=(y); i--)
#define N 400010
#define ll long long
using namespace std;
ll read(){
char ch=getchar(); ll x=0; int op=1;
for (; !isdigit(ch); ch=getchar()) if (ch=='-') op=-1;
for (; isdigit(ch); ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*op;
}
int n,m,tot,rt,now,clk,a[N],f[N][20],pos[N],in[N],out[N];
ll g[N],h[N],f1s[N],f2s[N],gs[N],hs[N],ans,dep[N];
struct seg{
int l,r,ls,rs,mid;
seg(){ l=r=ls=rs=mid=0; }
}tr[N];
void build(int &o,int l,int r,int d,int fa,bool fl){
o=++tot; in[o]=++clk; tr[o].l=l; tr[o].r=r; dep[o]=d; f[o][0]=fa;
if (!fl){//左儿子
g[o]=g[fa]+d; gs[o]=gs[fa]+1;
h[o]=h[fa]; hs[o]=hs[fa];
f1s[o]=f1s[fa]+d-1; f2s[o]=f2s[fa];
} else{
g[o]=g[fa]; gs[o]=gs[fa];
h[o]=h[fa]+d; hs[o]=hs[fa]+1;
f1s[o]=f1s[fa]; f2s[o]=f2s[fa]+d-1;
}
if (l==r){ pos[l]=o; out[o]=++clk; return; } int k=a[now]; tr[o].mid=k;
if (l<k) now++; build(tr[o].ls,l,k,d+1,o,0);
if (k+1<r) now++; build(tr[o].rs,k+1,r,d+1,o,1);
out[o]=++clk;
}
void print_tree(int u){
printf("id: %d, l=%d, r=%d\n",u,tr[u].l,tr[u].r);
if (tr[u].ls) print_tree(tr[u].ls);
if (tr[u].rs) print_tree(tr[u].rs);
}
int lca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
int tmp=dep[x]-dep[y];
per (i,18,0) if (tmp>>i&1) x=f[x][i];
if (x==y) return x;
per (i,18,0) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
bool isanc(int x,int y){ return in[x]<=in[y] && out[x]>=out[y]; }
int main(){
/*freopen("B.in","r",stdin);
freopen("B.out","w",stdout);*/
n=read();
rep (i,1,n-1) a[i]=read();
m=read();
now=1; build(rt,1,n,0,0,1);
rep (j,1,18) rep (i,1,tot) f[i][j]=f[f[i][j-1]][j-1];
while (m--){
int u=read(),l=read(),r=read(),x,y,z,w,v,tmp;
ans=0;
if (l==1 && r==n){ printf("%d\n",dep[u]); continue; }
x=pos[l-1]; y=pos[r+1]; tmp=lca(x,y); z=tr[tmp].ls; if (l==1 || r==1) z=1;
if (l!=1){
w=lca(x,u);
if (isanc(w,z)){
ll sum=gs[x]-gs[z];
ans+=g[x]-g[z]+sum*dep[u]-2ll*sum*dep[w];
if (w==z && isanc(tr[w].rs,u)) ans-=2;
} else{
ll sum=gs[x]-gs[w];
ans+=g[x]-g[w]+sum*dep[u]-2ll*sum*dep[w];
sum=gs[w]-gs[z];
ans+=g[w]-g[z]+sum*dep[u]-2ll*(f1s[w]-f1s[z]);
if (isanc(tr[w].rs,u)) ans-=2;
}
}
z=tr[tmp].rs; if (l==1 || r==1) z=1;
if (r!=n){
w=lca(y,u);
if (isanc(w,z)){
ll sum=hs[y]-hs[z];
ans+=h[y]-h[z]+sum*dep[u]-2ll*sum*dep[w];
if (w==z && isanc(tr[w].ls,u)) ans-=2;
} else{
ll sum=hs[y]-hs[w];
ans+=h[y]-h[w]+sum*dep[u]-2ll*sum*dep[w];
sum=hs[w]-hs[z];
ans+=h[w]-h[z]+sum*dep[u]-2ll*(f2s[w]-f2s[z]);
if (isanc(tr[w].ls,u)) ans-=2;
}
}
printf("%lld\n",ans);
}
return 0;
}