LCA题解
神奇化简题,谁看得出这是树剖。
首先,\(lca\)的祖宗对\(lca\)都有贡献,我们不妨将\(0\)到\(lca\)路径的权值都加\(1\),则\(lca\)的\(deep\)就为\(0\)到\(lca\)的路径权值和
则\(\sum_{i=l}^{r}deep[lca_{i}]=\sum_{i=l}^{r}\sum_{j=lca_{i}}^{j\epsilon path(0,lca_{i})}w[j]\)
我们想想在不会\(lca\)的时候如何暴力搞两个点\(x\)和\(y\)的l\(ca\)?
我们可以明确深度大于\(lca\)的点对于\(lca\)不会有贡献,且\(deep[x]>=deep[lca]\)
不妨将我们不妨将\(0\)到\(x\)路径的权值都加\(1\),\(deep[lca]=\sum_{i=y}^{i\epsilon path(0,y)}w[i]\)
感性理解一下吧
如此我们需要枚举\(i\)从\(l\)到\(r\),将\(0\)到\(i\)路径的权值都加\(1\),\(\sum_{i=l}^{r}deep[lca(i,z)]=\sum_{i=z}^{i\epsilon path(0,z)}w[i]\)
这样一次查询时间复杂度为\(O(n*logn*logn)\)
有\(q\)次询问。
我们想,每一次询问都把线段树清空太浪费了,区间\([l,r]\)可以差分为\([1,r]-[1,l-1]\)
将询问拆成\([1,l-1]\)和\([1,r]\)两部分,按右端点排序即可.
代码:
#include<bits/stdc++.h> #define lc (x<<1) #define rc (x<<1|1) using namespace std; const int N=60006,inf=1e9,mod=201314; int n,m,t1,t2,t3,cnt=0,dfn=0,tot=0,f[N],a[N],d[N],son[N],siz[N],num[N],top[N],lazy[N<<2],w[N<<2],head[N],ans[N]; struct xd{int i,flag,x,y;}q[N<<1]; struct edge{int nxt,to;}e[N<<1]; inline void add(int u,int v){e[++cnt].nxt=head[u],e[cnt].to=v,head[u]=cnt;} inline int read(){ int T=0,F=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();} while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar(); return F*T; } void dfs(int x){ d[x]=d[f[x]]+1,siz[x]=1; int maxt=0,maxa=-1; for(int i=head[x];i;i=e[i].nxt){ dfs(e[i].to),siz[x]+=siz[e[i].to]; if(maxa<siz[e[i].to]) maxt=e[i].to,maxa=siz[maxt]; } son[x]=maxt; } void dfs2(int x,int topx){ num[x]=++dfn,top[x]=topx; if(son[x]) dfs2(son[x],topx); for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=f[x]&&e[i].to!=son[x]) dfs2(e[i].to,e[i].to); } void pushdown(int x,int len){if(lazy[x]) lazy[lc]=(lazy[lc]+lazy[x])%mod,lazy[rc]=(lazy[rc]+lazy[x])%mod,w[lc]=(w[lc]+lazy[x]*(len-(len>>1))%mod)%mod,w[rc]=(w[rc]+lazy[x]*(len>>1)%mod)%mod,lazy[x]=0;} void update(int l,int r,int p,int q,int x,int y){ if(p<=l&&r<=q){w[x]=(w[x]+(r-l+1)*y%mod)%mod,lazy[x]=(lazy[x]+y)%mod; return;} pushdown(x,r-l+1); int mid=l+r>>1; if(p<=mid) update(l,mid,p,q,lc,y); if(q>mid) update(mid+1,r,p,q,rc,y); w[x]=(w[lc]+w[rc])%mod; } int query(int l,int r,int p,int q,int x){ if(p<=l&&r<=q) return w[x]; pushdown(x,r-l+1); int mid=l+r>>1,ans=0; if(p<=mid) ans=query(l,mid,p,q,lc); if(q>mid) ans=(ans+query(mid+1,r,p,q,rc))%mod; return ans; } void lj_update(int x,int y){ while(top[x]!=top[y]){ if(d[top[x]]<d[top[y]]) swap(x,y); update(1,n,num[top[x]],num[x],1,1); x=f[top[x]]; } if(d[x]>d[y]) swap(x,y); update(1,n,num[x],num[y],1,1); } int lj_query(int x,int y){ int ans=0; while(top[x]!=top[y]){ if(d[top[x]]<d[top[y]]) swap(x,y); ans=(ans+query(1,n,num[top[x]],num[x],1))%mod; x=f[top[x]]; } if(d[x]>d[y]) swap(x,y); ans=(ans+query(1,n,num[x],num[y],1))%mod; return ans; } bool cmp(xd u,xd v){return u.x<v.x;} int main(){ n=read(),m=read(); for(int i=2;i<=n;++i) f[i]=read()+1,add(f[i],i); dfs(1),dfs2(1,1); for(int i=1;i<=m;++i){ t1=read(),t2=read()+1,t3=read()+1; q[++tot].flag=0,q[tot].i=i,q[tot].x=t1,q[tot].y=t3; q[++tot].flag=1,q[tot].i=i,q[tot].x=t2,q[tot].y=t3; } sort(q+1,q+tot+1,cmp),q[0].x=0; for(int i=1;i<=tot;++i){ for(int j=q[i-1].x+1;j<=q[i].x;++j) lj_update(1,j); if(!q[i].flag) ans[q[i].i]=-lj_query(1,q[i].y); else ans[q[i].i]=(ans[q[i].i]+lj_query(1,q[i].y)+mod)%mod; } for(int i=1;i<=m;++i) printf("%d\n",ans[i]); return 0; }