题目链接:
A-[LNOI2014]LCA_牛客竞赛数据结构专题班树链剖分 (nowcoder.com)
题意:
1≤n≤50000,1≤m≤50000
样例:
输入
5 2
0
0
1
1
1 4 3
1 4 2
输出
8
5
思路:
本题的关键在于深度的一个巧妙转化,我们假设树上所有点的初始权值为0,对于点x,我们将从根节点到x路径上所有点的点权加1,对于x和z的最近公共祖先的深度就是从根节点到z的路径上的点权之和,自己也可以在纸上画画。
明确了这个转化之后,对于[l,r]与z的深度之和就等于 [1,r]与z的最近公共祖先的深度之和 减去 [1,l-1]与z的最近公共祖先的深度之和,设置一个结构体类型包括端点,z,询问的id及是否为查询右端点(type==-1表示为查询的左端点,==1表示为查询的右端点,这样方便直接查询后直接左减右加ans[id]+=type*ask()),对于第i个查询l r z,就有两个结构体变量{l-1,z,i,-1}和{r,z,i,1}。然后将所有的结构体变量按端点从小到大排序,用一个循环从i=0开始,每次将从根节点到i的路径上的权值加1,然后对所有端点为i的结构体变量查询z到根节点路径上的权值和,修改对应的ans[id]。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD=201314;
const int N=5e4+10;
ll ans[N];
int h[N],to[2*N],ne[2*N],cnt;
int sz[N],dep[N],fa[N],son[N];
int top[N],id[N],dfn;
int root;
typedef struct Node{
int l,r;
ll sum,lz;
}Node;
Node tr[N*4];
void pushup(int u){
tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%MOD;
}
void init_lazy(int u){
tr[u].lz=0;
}
void lazy_union(int fat,int u){
ll x=tr[fat].lz,len=tr[u].r-tr[u].l+1;
tr[u].sum=(tr[u].sum+len*x%MOD)%MOD;
tr[u].lz=(tr[u].lz+x)%MOD;
}
void pushdown(int u){
if(tr[u].lz!=0){
lazy_union(u,u<<1);
lazy_union(u,u<<1|1);
init_lazy(u);
}
}
void build(int u,int l,int r){
tr[u]={l,r,0,0};
if(l==r){
return ;
}
int mid=(l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int l,int r,ll x){
if(tr[u].l>=l&&tr[u].r<=r){
ll len=tr[u].r-tr[u].l+1;
tr[u].sum=(tr[u].sum+len*x%MOD)%MOD;
tr[u].lz=(tr[u].lz+x)%MOD;
return ;
}
pushdown(u);
int mid=(tr[u].l+tr[u].r)>>1;
if(l<=mid)
modify(u<<1,l,r,x);
if(r>mid)
modify(u<<1|1,l,r,x);
pushup(u);
}
ll query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u].sum;
}
pushdown(u);
int mid=(tr[u].l+tr[u].r)>>1;
ll res=0;
if(l<=mid)
res+=query(u<<1,l,r);
if(r>mid)
res=(res+query(u<<1|1,l,r))%MOD;
return res;
}
typedef struct Segment{
int r,z,id,type;
}Segment;
Segment seg[N*2];
bool cmp(Segment x,Segment y){
return x.r<y.r;
}
void init(){
dfn=0;
cnt=0;
memset(h,-1,sizeof(h));
}
void add_edge(int u,int v){
to[cnt]=v;
ne[cnt]=h[u];
h[u]=cnt++;
}
//轻重
void dfs1(int u,int fat){
sz[u]=1;
dep[u]=dep[fat]+1;
fa[u]=fat;
for(int i=h[u];i!=-1;i=ne[i]){
int v=to[i];
if(v!=fat){
dfs1(v,u);
sz[u]+=sz[v];
if(!son[u]||sz[son[u]]<sz[v]){
son[u]=v;
}
}
}
}
//链剖分
void dfs2(int u,int tp){
id[u]=++dfn;
top[u]=tp;
if(son[u])
dfs2(son[u],tp);
for(int i=h[u];i!=-1;i=ne[i]){
if(to[i]!=fa[u]&&to[i]!=son[u]){
dfs2(to[i],to[i]);
}
}
}
void add(int u){
while(top[u]!=root){
modify(1,id[top[u]],id[u],1);
u=fa[top[u]];
}
modify(1,id[root],id[u],1);
}
ll ask(int u){
ll res=0;
while(top[u]!=root){
res=(res+query(1,id[top[u]],id[u]))%MOD;
u=fa[top[u]];
}
res=(res+query(1,id[root],id[u]))%MOD;
return res;
}
int main(){
int n,q,u;
scanf("%d%d",&n,&q);
init();
for(int i=2;i<=n;i++){
scanf("%d",&u);
u++;
add_edge(u,i);
}
dfs1(1,0);
dfs2(1,1);
root=1;
int l,r,z;
int cn=0;
for(int i=1;i<=q;i++){
scanf("%d%d%d",&l,&r,&z);
r++,z++;
seg[++cn]={l,z,i,-1};
seg[++cn]={r,z,i,1};
}
sort(seg+1,seg+cn+1,cmp);
int j=1;
while(j<=cn&&seg[j].r==0)
j++;
build(1,1,n);
for(int i=1;i<=n;i++){
add(i);
while(j<=cn&&seg[j].r==i){
ll res=ask(seg[j].z);
int id=seg[j].id;
ans[id]=((ans[id]+res*seg[j].type)%MOD+MOD)%MOD;
j++;
}
}
for(int i=1;i<=q;i++){
printf("%lld\n",ans[i]);
}
}