- 题意
- 有一个很巧妙的转化:求x,y的dep[lca(x,y)]可以先x到根的路径上加1,然后查询y到根路径上的和。
所以x->[l,r]的lca和,可以转化为,先把[l,r]中每个到根的路径+1,再求一次x到根的前缀和。
然后[l,r]这种显然可以差分为两个询问[1..r]和[1..l-1],因此先把询问离线,用树链剖分维护区间加和求和,从前到后枚举每个前缀,每次修改+更新这个前缀的询问。具体见code
- code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long ll;
int ls[N],ed[N],rs[N],Tcnt,rt[N],fa[N],nxt[N],to[N],head[N],ecnt,b[N];
int Time,dfn[N],sz[N],son[N],top[N];
ll ans[N];
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
struct query {int opt,x,id;};
vector<query> V[N];
void gt_son(int u) {
sz[u]=1;
for(int i=head[u];i;i=nxt[i]) {
int v(to[i]);
gt_son(v);sz[u]+=sz[v];
if(sz[son[u]]<sz[v])son[u]=v;
}
}
void gt_top(int u,int Tp) {
top[u]=Tp;ed[Tp]=u;dfn[u]=++Time;b[Time]=u;
if(son[u])gt_top(son[u],Tp);
for(int i=head[u];i;i=nxt[i]) {
int v(to[i]);if(v==son[u])continue;
gt_top(v,v);
}
}
struct seg {int l,r,len;ll cnt,lazy;}T[N];
void P_up(int x) {T[x].cnt=(T[ls[x]].cnt+T[rs[x]].cnt);}
void P_dw(int x) {
if(!T[x].lazy)return;ll ad=T[x].lazy;T[x].lazy=0;
T[ls[x]].cnt+=ad*T[ls[x]].len;T[ls[x]].lazy+=ad;T[rs[x]].cnt+=ad*T[rs[x]].len,T[rs[x]].lazy+=ad;
}
void Build(int &x,int l,int r) {
x=++Tcnt;T[x]=(seg){l,r,r-l+1,0};
if(l==r)return;
int mid=(l+r)>>1;
Build(ls[x],l,mid),Build(rs[x],mid+1,r);
}
void Update(int x,int l,int r,int w) {
if(l<=T[x].l&&T[x].r<=r) {T[x].cnt+=w*T[x].len;T[x].lazy+=w;return;}
P_dw(x);
int mid=(T[x].l+T[x].r)>>1;
if(l<=mid)Update(ls[x],l,r,w);
if(r>mid)Update(rs[x],l,r,w);
P_up(x);
}
ll Sum(int x,int l,int r) {
if(l<=T[x].l&&T[x].r<=r) return T[x].cnt;
P_dw(x);
int mid=(T[x].l+T[x].r)>>1;ll res=0;
if(l<=mid)res+=Sum(ls[x],l,r);
if(r>mid)res+=Sum(rs[x],l,r);
return res;
}
ll Ask(int v) {
ll res=0;
while(v) {
int u=top[v];res+=Sum(rt[u],dfn[u],dfn[v]);v=fa[u];
}
return res;
}
void Update(int v) {
while(v) {
int u=top[v];Update(rt[u],dfn[u],dfn[v],1);v=fa[u];
}
}
int main() {
int n,m;scanf("%d%d",&n,&m);
for(int i=2;i<=n;i++) {scanf("%d",&fa[i]);fa[i]++;add_edge(fa[i],i);}
gt_son(1);
gt_top(1,1);
for(int i=1;i<=n;i++) {
int j=dfn[ed[b[i]]];
Build(rt[b[i]],i,j);i=j;
}
for(int i=1;i<=m;i++) {
int l,r,x;scanf("%d%d%d",&l,&r,&x);
l++;r++;x++;
if(l>1)V[l-1].push_back((query){-1,x,i});
V[r].push_back((query){1,x,i});
}
for(int i=1;i<=n;i++) {
Update(i);
for(int j=0;j<V[i].size();j++) {
query p=V[i][j];
ans[p.id]+=p.opt*Ask(p.x);
}
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]%201314);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人