题目描述:
给出一颗树
根节点为 1
给出 M 个 询问
l r z
求
∑ri=ldep[lca[i,z]]
∑
i
=
l
r
d
e
p
[
l
c
a
[
i
,
z
]
]
dep为距离根节点的距离+1
题目分析:
暴力分一点不给差评!
思路来自黄学长
首先 考虑一种暴力
我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。
观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用 [1, r] − [1, l − 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n − 1 依次插入点 i,即将 i 到根的路径上的点全部+1。离线询问答案即可。
资瓷这样的显然是树剖或者LCT
树剖O((n+q)*logn*logn)
题目链接:
Ac 代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#define ls (o<<1)
#define rs (o<<1)|1
const int mod=201314;
const int maxm=200005;
struct node1{
int p,flag,id;
}ask[maxm*2];
struct node2{
int z,ans[2];
}q[maxm];
struct node3{
int add,sum,sizx;
}st[maxm<<2];
int deep[maxm],son[maxm],top[maxm],id[maxm],fa[maxm],siz[maxm],tot,num;
int head[maxm],to[maxm],net[maxm],cnt;
int n,m;
void addedge(int u,int v)
{
cnt++;
to[cnt]=v,net[cnt]=head[u],head[u]=cnt;
}
void dfs1(int now,int fax,int dep)
{
deep[now]=dep,fa[now]=fax,siz[now]=1;
for(int i=head[now];i;i=net[i])
if(to[i]!=fax)
{
dfs1(to[i],now,dep+1);
siz[now]+=siz[to[i]];
if(siz[to[i]]>siz[son[now]]) son[now]=to[i];
}
}
void dfs2(int now,int topx)
{
top[now]=topx;
id[now]=++tot;
if(son[now]) dfs2(son[now],topx);
for(int i=head[now];i;i=net[i])
if(!id[to[i]])
dfs2(to[i],to[i]);
}
void updata(int o){st[o].sum=(st[ls].sum+st[rs].sum)%mod;}
void col(int o,int num)
{
int sizx=st[o].sizx;
st[o].add=(st[o].add+num)%mod;
st[o].sum=(st[o].sum+1ll*sizx*num%mod)%mod;
}
void pushdown(int o)
{
if(st[o].add)
{
col(ls,st[o].add);
col(rs,st[o].add);
st[o].add=0;
}
}
void build(int o,int l,int r)
{
st[o].sizx=(r-l+1);
if(l>=r) return;
int mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
}
void change(int o,int l,int r,int ql,int qr,int num)
{
if(ql<=l&&r<=qr)
{
col(o,num);
return;
}
pushdown(o);
int mid=(l+r)>>1;
if(ql<=mid) change(ls,l,mid,ql,qr,num);
if(qr>mid) change(rs,mid+1,r,ql,qr,num);
updata(o);
}
int asksum(int o,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) return st[o].sum;
pushdown(o);
int mid=(l+r)>>1;
int ans1=0,ans2=0;
if(ql<=mid) ans1=asksum(ls,l,mid,ql,qr);
if(qr>mid) ans2=asksum(rs,mid+1,r,ql,qr);
return (ans1+ans2)%mod;
}
inline void addtree(int u,int v)
{
while(top[u]!=top[v])
{
if(deep[top[u]]<deep[top[v]]) std::swap(u,v);
change(1,1,n,id[top[u]],id[u],1);
u=fa[top[u]];
}
if(deep[u]<deep[v]) std::swap(u,v);
change(1,1,n,id[v],id[u],1);
}
inline int asktree(int u,int v)
{
int ans=0;
while(top[u]!=top[v])
{
if(deep[top[u]]<deep[top[v]]) std::swap(u,v);
ans=(ans+asksum(1,1,n,id[top[u]],id[u])%mod)%mod;
u=fa[top[u]];
}
if(deep[u]<deep[v]) std::swap(u,v);
ans=(ans+asksum(1,1,n,id[v],id[u])%mod)%mod;
return ans%mod;
}
bool comp(node1 x,node1 y)
{
return x.p<y.p;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=2;i<=n;i++)
{
int x;
scanf("%d",&x);
x++;
addedge(x,i);
}
for(int i=1;i<=m;i++)
{
int l,r,z;
scanf("%d%d%d",&l,&r,&z);
l++,r++,z++;
q[i].z=z;
ask[++num].p=l-1,ask[num].flag=0,ask[num].id=i;
ask[++num].p=r,ask[num].flag=1,ask[num].id=i;
}
build(1,1,n);
std::sort(ask+1,ask+num+1,comp);
dfs1(1,0,0),dfs2(1,1);
int now=0;
for(int i=1;i<=num;i++)
{
while(now<ask[i].p) addtree(++now,1);
int x=ask[i].id;
int d;
q[x].ans[ask[i].flag]=d=asktree(q[x].z,1);
//printf("%d\n",d);
}
for(int i=1;i<=m;i++) printf("%d\n",(q[i].ans[1]-q[i].ans[0]+mod)%mod);
return 0;
}