树上技巧
1.与 \(LCA\) 深度有关的查询
两个点的 \(LCA\) 的深度可以转化为,将其中一个点到根的路径上每个点的点权均加 \(1\) 然后查询另一个点到根的点权和
P4211 [LNOI2014] LCA
Description
给出一个 \(n\) 个节点的有根树(编号为 \(0\) 到 \(n-1\),根节点为 \(0\))。
一个点的深度定义为这个节点到根的距离 \(+1\)。
设 \(dep[i]\) 表示点 \(i\) 的深度,\(\operatorname{LCA}(i, j)\) 表示 \(i\) 与 \(j\) 的最近公共祖先。
有 \(m\) 次询问,每次询问给出 \(l, r, z\),求 \(\sum_{i=l}^r dep[\operatorname{LCA}(i,z)]\) 。
\(1\le n\le 50000,1\le m\le 50000\)
Solution
先离线,对查询差分,然后按照 \(1~n\) 的顺序路径加并同时处理查询。
路径加使用树剖,时间复杂度 \(O(nlogn)\)。
#include<bits/stdc++.h>
#define pb push_back
#define N 50005
#define mid ((l+r)>>1)
#define mod 201314
using namespace std;
int n,m,f[N],ans[N],rk[N],tot,top[N],dep[N],son[N],siz[N];
int tag[N<<2],sum[N<<2];
struct query{int num,x,f;};
vector<query>q[N];
vector<int>t[N];
inline void dfs(int u){
dep[u]=dep[f[u]]+1,siz[u]=1,son[u]=n;
for(int v:t[u])dfs(v),siz[u]+=siz[v],siz[v]>siz[son[u]]?son[u]=v:0;
}
inline void dfs(int u,int tp){
rk[u]=++tot,top[u]=tp;
if(son[u]!=n)dfs(son[u],tp);
for(int v:t[u])if(v!=son[u])dfs(v,v);
}
inline int ls(int p){return p<<1;}
inline int rs(int p){return p<<1|1;}
inline void F(int p,int l,int r,int k){
sum[p]+=(r-l+1)*k;
tag[p]+=k;
}
inline void push_down(int p,int l,int r){
if(!tag[p])return;
F(ls(p),l,mid,tag[p]);
F(rs(p),mid+1,r,tag[p]);
tag[p]=0;
}
inline void push_up(int p){sum[p]=sum[ls(p)]+sum[rs(p)];}
inline void addsum(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return F(p,l,r,1),void();
push_down(p,l,r);
if(L<=mid)addsum(ls(p),l,mid,L,R);
if(R>mid)addsum(rs(p),mid+1,r,L,R);
push_up(p);
}
inline int getsum(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return sum[p];
push_down(p,l,r);
int res=0;
if(L<=mid)res+=getsum(ls(p),l,mid,L,R);
if(R>mid)res+=getsum(rs(p),mid+1,r,L,R);
return res;
}
inline int op(int u){
int res=0;
for(;~u;u=f[top[u]])res+=getsum(1,1,n,rk[top[u]],rk[u]);
return res;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m,f[0]=-1;
for(int i=1;i<n;i++)cin>>f[i],t[f[i]].pb(i);
for(int i=0,l,r,z;i<m;i++)
cin>>l>>r>>z,q[r].pb({i,z,1}),~(l-1)?q[l-1].pb({i,z,-1}):void();
dfs(0),dfs(0,0);
for(int i=0;i<n;i++){
for(int j=i;~j;j=f[top[j]])addsum(1,1,n,rk[top[j]],rk[j]);
for(auto j:q[i])ans[j.num]+=j.f*op(j.x);
}
for(int i=0;i<m;i++)cout<<ans[i]%mod<<'\n';
}
P5305 [GXOI/GZOI2019] 旧词
Description
给定一棵 \(n\) 个点的有根树,节点标号 \(1 \sim n\),\(1\) 号节点为根。
给定常数 \(k\)。
给定 \(Q\) 个询问,每次询问给定 \(x,y\)。
求:
\(\text{lca}(x,y)\) 表示节点 \(x\) 与节点 \(y\) 在有根树上的最近公共祖先。
\(\text{depth}(x)\) 表示节点 \(x\) 的深度,根节点的深度为 \(1\)。
由于答案可能很大,你只需要输出答案模 \(998244353\) 的结果。
\(n \le 50000,Q \le 50000,1 \le k \le 10^9\)
Solution
跟上一题很类似,容易想到求深度的次方只需要把之前操作中“路径点加1”改成“对于路径点 \(x\) ,加 \(dep(x)^k-(dep(x)-1)^k\) 即可。
那么树剖之后预处理每个点的系数放进线段树里。
时间复杂度 \(O(nlogn)\)。
#include<bits/stdc++.h>
#define pb push_back
#define N 50005
#define mid ((l+r)>>1)
#define mod 998244353
using namespace std;
int n,m,f[N],ans[N],rk[N],tot,top[N],dep[N],son[N],siz[N],K,dfn[N];
int tag[N<<2],sum[N<<2],a[N<<2];
struct query{int num,x;};
vector<query>q[N];
vector<int>t[N];
inline int mo(int x){return x<mod?x:x-mod;}
inline void dfs(int u){
dep[u]=dep[f[u]]+1,siz[u]=1;
for(int v:t[u])dfs(v),siz[u]+=siz[v],siz[v]>siz[son[u]]?son[u]=v:0;
}
inline void dfs(int u,int tp){
rk[u]=++tot,top[u]=tp,dfn[tot]=u;
if(son[u])dfs(son[u],tp);
for(int v:t[u])if(v!=son[u])dfs(v,v);
}
inline int ls(int p){return p<<1;}
inline int rs(int p){return p<<1|1;}
inline void F(int p,int k){
sum[p]=(sum[p]+1ll*a[p]*k)%mod;
tag[p]=mo(tag[p]+k);
}
inline void push_down(int p){
if(!tag[p])return;
F(ls(p),tag[p]);
F(rs(p),tag[p]);
tag[p]=0;
}
inline void push_up(int p){sum[p]=sum[ls(p)]+sum[rs(p)];}
inline void addsum(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return F(p,1),void();
push_down(p);
if(L<=mid)addsum(ls(p),l,mid,L,R);
if(R>mid)addsum(rs(p),mid+1,r,L,R);
push_up(p);
}
inline int getsum(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return sum[p];
push_down(p);
int res=0;
if(L<=mid)res+=getsum(ls(p),l,mid,L,R);
if(R>mid)res+=getsum(rs(p),mid+1,r,L,R);
return res;
}
inline int ksm(int a,int b){
int res=1;
while(b){
if(b&1)res=1ll*res*a%mod;
a=1ll*a*a%mod,b>>=1;
}
return res;
}
inline void build(int p,int l,int r){
if(l==r)return a[p]=mo(ksm(dep[dfn[l]],K)-ksm(dep[dfn[l]]-1,K)+mod),void();
build(ls(p),l,mid);
build(rs(p),mid+1,r);
a[p]=mo(a[ls(p)]+a[rs(p)]);
}
inline int op(int u){
int res=0;
for(;u;u=f[top[u]])res+=getsum(1,1,n,rk[top[u]],rk[u]);
return res;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>K;
for(int i=2;i<=n;i++)cin>>f[i],t[f[i]].pb(i);
dfs(1),dfs(1,1);build(1,1,n);
for(int i=1,l,x;i<=m;i++)cin>>l>>x,q[l].pb({i,x});
for(int i=1;i<=n;i++){
for(int j=i;j;j=f[top[j]])addsum(1,1,n,rk[top[j]],rk[j]);
for(auto j:q[i])ans[j.num]=op(j.x);
}
for(int i=1;i<=m;i++)cout<<ans[i]%mod<<'\n';
}

浙公网安备 33010602011771号