传送门
对于这种广义线段树的题,想要定位区间
[
l
,
r
]
[l,r]
[l,r]可以转化成定位节点
[
l
−
1
,
l
−
1
]
,
[
r
+
1
,
r
+
1
]
[l-1,l-1],[r+1,r+1]
[l−1,l−1],[r+1,r+1]路径上的一些儿子节点。
设
l
c
a
=
t
lca=t
lca=t
那么路径
[
l
−
1
,
l
−
1
]
→
t
[l-1,l-1]\rightarrow t
[l−1,l−1]→t的右儿子和路径
[
r
+
1
,
r
+
1
]
→
t
[r+1,r+1]\rightarrow t
[r+1,r+1]→t的左儿子就是被定位到的区间。
于是就可以树上差分了。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
typedef long long ll;
const int N=4e5+5;
int Mid[N],n;
vector<int>e[N];
namespace sgt{
int son[N][2],id[N],tot=0,cnt=0,siz[N],fa[N],dep[N],top[N],hson[N];
ll disl[N],disr[N],dissl[N],dissr[N],sizl[N],sizr[N];
int build(int l,int r){
int p=++tot;
if(l==r)return id[l]=p;
int mid=Mid[++cnt];
return son[p][0]=build(l,mid),son[p][1]=build(mid+1,r),p;
}
void dfs1(int p){
siz[p]=1;
for(ri i=0,v;i<2;++i){
if(!(v=son[p][i]))continue;
fa[v]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
if(siz[v]>siz[hson[p]])hson[p]=v;
}
}
void dfs2(int p,int tp){
top[p]=tp;
if(!hson[p])return;
dfs2(hson[p],tp);
for(ri i=0,v;i<2;++i)if((v=son[p][i])>0&&v!=hson[p])dfs2(v,v);
}
void dfs(int p){
dissl[p]+=dissl[fa[p]];
dissr[p]+=dissr[fa[p]];
disl[p]+=disl[fa[p]],sizl[p]+=sizl[fa[p]];
disr[p]+=disr[fa[p]],sizr[p]+=sizr[fa[p]];
if(son[p][1]){
disl[p]+=dep[son[p][1]],disl[son[p][1]]-=dep[son[p][1]];
dissl[p]+=dep[p],dissl[son[p][1]]-=dep[p];
++sizl[p],--sizl[son[p][1]];
}
if(son[p][0]){
disr[p]+=dep[son[p][0]],disr[son[p][0]]-=dep[son[p][0]];
dissr[p]+=dep[p],dissr[son[p][0]]-=dep[p];
++sizr[p],--sizr[son[p][0]];
}
if(son[p][0])dfs(son[p][0]);
if(son[p][1])dfs(son[p][1]);
}
inline int lca(int x,int y){
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
inline void init(){
build(1,n);
int p1=++tot,p2=++tot,p3=++tot,rt=++tot;
id[0]=p1;
son[rt][0]=p1,son[rt][1]=p2;
id[n+1]=p3;
son[p2][0]=1,son[p2][1]=p3;
dfs1(rt),dfs2(rt,rt);
dfs(rt);
}
inline ll calc1(int p,int x){
ll ret=disl[x]+sizl[x]*dep[p];
int t=lca(p,x);
ret-=(sizl[x]-sizl[t])*dep[t]*2;
if(son[t][1])if(lca(son[t][1],p)==son[t][1]&&lca(son[t][1],x)!=son[t][1])ret-=2;
ret-=dissl[t]*2;
return ret;
}
inline ll calc2(int p,int x){
ll ret=disr[x]+sizr[x]*dep[p];
int t=lca(p,x);
ret-=(sizr[x]-sizr[t])*dep[t]*2;
if(son[t][0])if(lca(son[t][0],p)==son[t][0]&&lca(son[t][0],x)!=son[t][0])ret-=2;
ret-=dissr[t]*2;
return ret;
}
inline void solve(int p,int l,int r){
int t=lca((l=id[l-1]),(r=id[r+1]));
cout<<calc1(p,l)-calc1(p,t)+calc2(p,r)-calc2(p,t)<<'\n';
}
}
int main(){
n=read();
for(ri i=1;i<n;++i)Mid[i]=read();
sgt::init();
for(ri u,l,r,tt=read();tt;--tt){
u=read(),l=read(),r=read();
sgt::solve(u,l,r);
}
return 0;
}

这篇博客详细介绍了如何利用线段树解决Zjoi2017中的一道问题,通过将区间[l, r]转化为寻找节点[l-1, l-1]和[r+1, r+1]路径上的特定子节点,然后应用树上差分策略来求解。博主分享了具体的解题思路和代码实现。"
139504731,7990474,GLM-4-9B模型推理实践:OpenAI接口与Requests调用,"['自然语言处理', '人工智能', '模型推理', '接口调用', 'GLM模型']
399

被折叠的 条评论
为什么被折叠?



