【题解 P4211】 LCA
[LNOI2014] LCA
题目描述
给出一个 \(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)]\) 。
输入格式
第一行 \(2\) 个整数,\(n, m\)。
接下来 \(n-1\) 行,分别表示点 \(1\) 到点 \(n-1\) 的父节点编号。
接下来 \(m\) 行,每行 \(3\) 个整数,\(l, r, z\)。
输出格式
输出 \(q\) 行,每行表示一个询问的答案。每个答案对 \(201314\) 取模输出。
样例 #1
样例输入 #1
5 2
0
0
1
1
1 4 3
1 4 2
样例输出 #1
8
5
提示
对于 \(20\%\) 的数据,\(n\le 10000,m\le 10000\);
对于 \(40\%\) 的数据,\(n\le 20000,m\le 20000\);
对于 \(60\%\) 的数据,\(n\le 30000,m\le 30000\);
对于 \(80\%\) 的数据,\(n\le 40000,m\le 40000\);
对于 \(100\%\) 的数据,\(1\le n\le 50000,1\le m\le 50000\)。
解题思路
一个点的深度还可以看成什么?还可以看成是这个点到根节点的点数。
那么,求 $\sum_{i=l}^r dep[\operatorname{LCA}(i,z)] $ ,相当于求对于每一个 \(l\le i \le r\),\(\operatorname{LCA}(i,z)\) 到根节点的点数。
由于都有一个 \(z\) ,我们可以看成 \(i\) 到根节点的路径与 \(z\) 到根节点共同的点数。
那我们可以把 \([l,r]\) 之间的每个数到根节点的路径上的点权都加上 \(1\) ,然后再求出 \(z\) 到根节点的点权和即可。
进一步优化,我们把每个问题区间拆成 \([1,l-1]\) 与 \([1,r]\) 两部分,每次只需将 \([1,x]\) 到根节点的路径上的点权值 \(+1\) 。
这些区间是有共同部分的,我们只需按右端点从小到大排序,每次加上后继续做下一个即可。
时间复杂度 \(O(mlog^2n)\) 。
Code
#include<bits/stdc++.h>
using namespace std;
long long mod=201314;
struct datay
{
long long x,y,p,v;
}l[100005];
long long n,m,son[50005],size[50005],fa[50005],deep[50005],q;
long long dfn[50005],top[50005],num;
long long f[200005],d[200005];
vector<long long> a[50005];
bool cmp(datay qw,datay er)
{
return qw.x<er.x;
}
bool cmp1(datay qw,datay er)
{
return qw.p<er.p;
}
void dfs1(long long x,long long y)
{
fa[x]=y;
deep[x]=deep[y]+1;
size[x]=1;
for(int i=0;i<a[x].size();i++)
{
if(a[x][i]==y)continue;
dfs1(a[x][i],x);
size[x]+=size[a[x][i]];
if(size[a[x][i]>size[son[x]]])son[x]=a[x][i];
}
return;
}
void dfs2(long long x,long long y)
{
dfn[x]=++num;
if(son[x])
{
top[son[x]]=top[x];
dfs2(son[x],x);
}
for(int i=0;i<a[x].size();i++)
{
if(a[x][i]==y||a[x][i]==son[x])continue;
top[a[x][i]]=a[x][i];
dfs2(a[x][i],x);
}
return;
}
void galaxy(long long x,long long l,long long r,long long v)
{
f[x]+=v*(r-l+1);
d[x]+=v;
return;
}
void up(long long x)
{
f[x]=f[x<<1]+f[(x<<1)|1];
return;
}
void pushdown(long long x,long long l,long long r)
{
if(d[x]==0)return;
long long lc=(x<<1),rc=(x<<1)|1,mid=(l+r)>>1;
galaxy(lc,l,mid,d[x]);
galaxy(rc,mid+1,r,d[x]);
d[x]=0;
return;
}
void dijah(long long x,long long l,long long r,long long ql,long long qr,long long v)
{
if(ql<=l&&r<=qr)
{
galaxy(x,l,r,v);
return;
}
pushdown(x,l,r);
long long lc=(x<<1),rc=(x<<1)|1,mid=(l+r)>>1;
if(ql<=mid)dijah(lc,l,mid,ql,qr,v);
if(qr>mid)dijah(rc,mid+1,r,ql,qr,v);
up(x);
return;
}
long long gaia(long long x,long long l,long long r,long long ql,long long qr)
{
if(ql<=l&&r<=qr)return f[x];
pushdown(x,l,r);
long long lc=(x<<1),rc=(x<<1)|1,mid=(l+r)>>1,h=0;
if(ql<=mid)h+=gaia(lc,l,mid,ql,qr);
if(qr>mid)h+=gaia(rc,mid+1,r,ql,qr);
return h;
}
void add(long long x,long long y,long long v)
{
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]])swap(x,y);
dijah(1,1,n,dfn[top[x]],dfn[x],v);
x=top[x];
x=fa[x];
}
if(deep[x]>deep[y])swap(x,y);
dijah(1,1,n,dfn[x],dfn[y],v);
}
long long gaia(long long x,long long y)
{
long long h=0;
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]])swap(x,y);
h+=gaia(1,1,n,dfn[top[x]],dfn[x]);
x=top[x];
x=fa[x];
}
if(deep[x]>deep[y])swap(x,y);
h+=gaia(1,1,n,dfn[x],dfn[y]);
return h;
}
int main()
{
long long x,y,z;
scanf("%lld%lld",&n,&m);
for(int i=2;i<=n;i++)
{
scanf("%lld",&x);
x++;
a[i].push_back(x);
a[x].push_back(i);
}
dfs1(1,0);
top[1]=1;
dfs2(1,0);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&x,&y,&z);
x++;
y++;
z++;
if(x>y)swap(x,y);
l[++q].x=x-1;
l[q].y=z;
l[q].p=2*i-1;
l[++q].x=y;
l[q].y=z;
l[q].p=2*i;
}
sort(l+1,l+q+1,cmp);
for(int i=1;i<=q;i++)
{
if(l[i].x==0)continue;
for(int j=l[i-1].x+1;j<=l[i].x;j++)add(1,j,1);
l[i].v=gaia(l[i].y,1);
}
sort(l+1,l+q+1,cmp1);
for(int i=1;i<=q;i+=2)
{
cout<<((l[i+1].v-l[i].v)%mod+mod)%mod<<'\n';
}
return 0;
}