题目描述:
网址:http://www.lydsy.com/JudgeOnline/problem.php?id=3626
大意:
给出一个n个节点的有根树(编号为0到n-1,根节点为0)。
一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求\(∑dep[LCA(i,z)],{l<=i<=r}\)。
---(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)
题目解法:
考虑求\(LCA(i,z)\),z是不变的。
那么如果把 \(i到root的路径\) 权值加1,最后答案就为 \(z到root路径\) 上的权值和。
这是一个常见的套路啦。
因为这个我们可以对每一次的询问进行差分。
查询答案变为\(Ans = Query(R) - Query(L-1);\)
这样就可以离线做:
我们从左往右依次增加 1~N到root的路径权值 ,然后 查询对应相关询问中 z到root路径 的权值和即可。
所有东西都直接用LCT维护即可。
实现代码
#include<bits/stdc++.h>
#define ll long long
#define RG register
#define IL inline
#define maxn 60000
#define mod 201314
using namespace std;
IL ll gi(){
RG ll date = 0, m = 1; RG char ch = 0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch = getchar();
if(ch == '-'){m = -1; ch = getchar();}
while(ch>='0' && ch<='9')
{date=date*10+ch-'0'; ch = getchar();}
return date*m;
}
ll N,Q,root; struct Question {ll L,R,Z;}qs[maxn];
ll ch[maxn][2],fa[maxn],rev[maxn],sum[maxn],sz[maxn],laz[maxn],val[maxn];
vector<ll>gpL[maxn],gpR[maxn]; ll stk[maxn];
IL bool Son(RG ll x){return ch[fa[x]][1] == x;}
IL bool Isroot(RG ll x){return (ch[fa[x]][1] != x && ch[fa[x]][0] != x);}
IL bool Reverse(RG ll x){swap(ch[x][0],ch[x][1]); rev[x]^=1;}
IL void PushUp(RG ll x){
sum[x] = ( val[x] + sum[ch[x][0]] + sum[ch[x][1]] )%mod;
sz[x] = 1 + sz[ch[x][0]] + sz[ch[x][1]];
}
IL void PushDown(RG ll x){
RG ll ls = ch[x][0],rs = ch[x][1];
if(laz[x]){
sum[ls] = (sum[ls] + 1ll*laz[x]*sz[ls])%mod;
sum[rs] = (sum[rs] + 1ll*laz[x]*sz[rs])%mod;
val[ls] = (val[ls] + laz[x])%mod;
val[rs] = (val[rs] + laz[x])%mod;
laz[ls] = (laz[x] + laz[ls])%mod;
laz[rs] = (laz[x] + laz[rs])%mod;
laz[x] = 0;
}
if(rev[x]){ Reverse(ls); Reverse(rs);rev[x] ^= 1; }
}
IL void Rot(RG ll x){
RG ll y = fa[x],z = fa[y],c = Son(x);
if(!Isroot(y))ch[z][Son(y)] = x; fa[x] = z;
ch[y][c] = ch[x][!c]; fa[ch[y][c]] = y;
ch[x][!c] = y; fa[y] = x; PushUp(y);
}
IL void Splay(RG ll x){
stk[++stk[0]] = x;
for(RG ll y = x; !Isroot(y); y = fa[y])stk[++stk[0]] = fa[y];
while(stk[0])PushDown(stk[stk[0]--]);
for(RG ll y = fa[x]; !Isroot(x); Rot(x),y = fa[x])
if(!Isroot(y))Son(x) ^ Son(y) ? Rot(x) : Rot(y);
PushUp(x);
}
IL void Access(RG ll x){ for(RG ll y = 0; x; y = x,x = fa[x])Splay(x),ch[x][1] = y,PushUp(x); }
IL void Makeroot(RG ll x){ Access(x); Splay(x); Reverse(x); }
IL void Split(RG ll x,RG ll y){ Makeroot(x); Access(y); Splay(y); }
IL void Link(RG ll x,RG ll y){ Makeroot(x); fa[x] = y; }
//已经把所有点的标号都加了1。
int main(){
N = gi(); Q = gi();
for(RG ll i = 1; i <= N; i ++)sz[i] = 1;
for(RG ll i = 2; i <= N ; i ++)Link(gi()+1,i);
for(RG ll i = 1; i <= Q ; i ++){
qs[i].L = (gi()-1)+1; qs[i].R = gi()+1; qs[i].Z = gi()+1;
gpL[qs[i].L].push_back(i);
gpR[qs[i].R].push_back(i);
}
root = 1;
for(RG ll i = 1; i <= N; i ++){
Split(root,i);
val[i]++; laz[i]++;
sum[i] = sum[i] + sz[i];
for(RG ll j = 0,c; j < gpL[i].size(); j ++)
c = gpL[i][j] ,Split(root,qs[c].Z) , qs[c].L = sum[qs[c].Z];
for(RG ll j = 0,c; j < gpR[i].size(); j ++)
c = gpR[i][j] ,Split(root,qs[c].Z) , qs[c].R = sum[qs[c].Z];
}
for(RG ll i = 1; i <= Q; i ++)
printf("%lld\n",(qs[i].R - qs[i].L + mod)%mod);
return 0;
}