Description
给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。
Solution
一开始就想偏了,越推越做不出来o(╯□╰)o
最初的想法是对于同属于一个子树内的节点他们和点z的lca是固定的,但是发现这样反而不好做
可以发现求[l,r]的答案可以拆成求[1,l-1]和[1,r]两部分,l和r的lca深度即为l和r到根节点路径的并的长度(可以画图理解
那么我们就离线所有的询问排序,用一种能在树上求路径和、树上修改路径的数据结构统计答案,lct或者树链剖分都行
这道题已经给定的每个节点的父亲,因此只用连单向边
Code
#include <stdio.h>
#include <algorithm>
#include <vector>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
const int N=50005;
const int E=50005;
struct edge{int x,y,next;}e[E];
struct Q{int x,z,v,id;};
std:: vector<Q> q;
int size[N],dep[N],pos[N],bl[N],fa[N];
int ans[N],sum[N<<2],lazy[N<<2];
int ls[N],edCnt=0;
int n,m;
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void addEdge(int x,int y) {
e[++edCnt]=(edge){x,y,ls[x]}; ls[x]=edCnt;
}
void dfs1(int now) {
size[now]=1;
for (int i=ls[now];i;i=e[i].next) {
dep[e[i].y]=dep[now]+1;
fa[e[i].y]=now;
dfs1(e[i].y);
size[now]+=size[e[i].y];
}
}
void dfs2(int now,int up) {
pos[now]=++pos[0]; bl[now]=up;
int mx=0;
for (int i=ls[now];i;i=e[i].next) {
if (size[e[i].y]>size[mx]) mx=e[i].y;
}
if (!mx) return ;
dfs2(mx,up);
for (int i=ls[now];i;i=e[i].next) {
if (e[i].y!=mx) dfs2(e[i].y,e[i].y);
}
}
void push_down(int now,int tl,int tr) {
if (!lazy[now]) return ;
lazy[now<<1]+=lazy[now];
lazy[now<<1|1]+=lazy[now];
int mid=(tl+tr)>>1;
sum[now<<1]+=(mid-tl+1)*lazy[now];
sum[now<<1|1]+=(tr-mid)*lazy[now];
lazy[now]=0;
}
void modify(int now,int tl,int tr,int l,int r,int v) {
if (tl==l&&tr==r) {
sum[now]+=(r-l+1)*v;
lazy[now]+=v;
return ;
}
push_down(now,tl,tr);
int mid=(tl+tr)>>1;
if (r<=mid) modify(now<<1,tl,mid,l,r,v);
else if (l>mid) modify(now<<1|1,mid+1,tr,l,r,v);
else {
modify(now<<1,tl,mid,l,mid,v);
modify(now<<1|1,mid+1,tr,mid+1,r,v);
}
sum[now]=sum[now<<1]+sum[now<<1|1];
}
void change(int x,int y,int v) {
while (bl[x]!=bl[y]) {
if (dep[bl[x]]<dep[bl[y]]) std:: swap(x,y);
modify(1,1,n,pos[bl[x]],pos[x],v);
x=fa[bl[x]];
}
if (pos[y]<pos[x]) std:: swap(x,y);
modify(1,1,n,pos[x],pos[y],v);
}
int query(int now,int tl,int tr,int l,int r) {
if (tl==l&&tr==r) return sum[now];
push_down(now,tl,tr);
int mid=(tl+tr)>>1;
if (r<=mid) return query(now<<1,tl,mid,l,r);
else if (l>mid) return query(now<<1|1,mid+1,tr,l,r);
else return query(now<<1,tl,mid,l,mid)+query(now<<1|1,mid+1,tr,mid+1,r);
}
int get_sum(int x,int y) {
int ret=0;
while (bl[x]!=bl[y]) {
if (dep[bl[x]]<dep[bl[y]]) std:: swap(x,y);
ret+=query(1,1,n,pos[bl[x]],pos[x]);
x=fa[bl[x]];
}
if (pos[y]<pos[x]) std:: swap(x,y);
ret+=query(1,1,n,pos[x],pos[y]);
return ret;
}
bool cmp(Q a,Q b) {return a.x<b.x;}
int main(void) {
n=read(),m=read();
rep(i,2,n) {
int x=read()+1;
addEdge(x,i);
}
dep[1]=1; dfs1(1); dfs2(1,1);
int cnt=0;
rep(i,1,m) {
int x=read()+1,y=read()+1,z=read()+1;
q.push_back((Q){x-1,z,-1,i});
q.push_back((Q){y,z,1,i});
}
std:: sort(q.begin(),q.end(),cmp);
int now=0;
rep(i,0,q.size()-1) {
while (now<q[i].x) {
now++;
change(1,now,1);
}
ans[q[i].id]+=q[i].v*get_sum(1,q[i].z);
}
rep(i,1,m) printf("%d\n", ans[i]%201314);
return 0;
}