题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3626
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的最近公共祖先的深度之和)
Input
第一行2个整数n q。
接下来n-1行,分别表示点1到点n-1的父节点编号。
接下来q行,每行3个整数l r z。
Output
输出q行,每行表示一个询问的答案。每个答案对201314取模输出
Sample Input
5 2
0
0
1
1
1 4 3
1 4 2
0
0
1
1
1 4 3
1 4 2
Sample Output
8
5
5
HINT
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。
题解:
树链剖分,虽然题目是lca,然而好像并没有什么关系。题目查询的是在l到r区间中,与z的最近公共祖先的深度之和。然后很明显可以知道两点到根节点经过的共同节点个数为其公共祖先的深度,比如样例中4和5共同经过1和2,而他们的最近公共祖先为2,其深度为2。然后求l到r之间的,可以转换为[1,l]-[1,r-1]的差。那么就可以对于查询离线,把询问分成两部分,按值来排序,比如[1,4,3],就分成[0,3]和[4,3]。查询时把需要加的点一个个加进去,当到达一个点恰好为某个询问的值,那么就可以把这个询问的答案来更新一下了。值的定义为分成两部分后区间的右端点,左端点恒为1。
代码:
#include<iostream> #include<cmath> #include<cstring> #include<cstdlib> #include<cstdio> #include<ctime> #include<queue> #include<vector> #include<algorithm> #define mo 201314 #define N 300010 using namespace std; long long sum,ans[N]; int tot,p,q,n,u,v; long long lazy[N],b[N]; int siz[N],fa[N],son[N],dep[N],head[N],top[N],w[N]; struct h1{int next,to;}tu[N]; struct data{int l,f,w,wei;}a[2*N]; int getint() { int res=0,w=1; char ch=getchar(); while ((ch>'9' || ch<'0')&&ch!='-') ch=getchar(); if (ch=='-') w=-1,ch=getchar(); while (ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar(); return res*w; } void link(int x,int y) { tu[++sum].next=head[x]; head[x]=sum; tu[sum].to=y; } void dfs1(int p) { siz[p]=1; for (int i=head[p];i;i=tu[i].next) { int v=tu[i].to; if (v==fa[p]) continue; dep[v]=dep[p]+1; dfs1(v); siz[p]+=siz[v]; if (siz[v]>siz[son[p]]) son[p]=v; } } void dfs2(int p,int t) { top[p]=t; w[p]=++tot; if (son[p]) dfs2(son[p],t); for (int i=head[p];i;i=tu[i].next) if (tu[i].to!=son[p]&&tu[i].to!=fa[p]) dfs2(tu[i].to,tu[i].to); } bool cmp(data a,data b) {return a.l<b.l;} void tree_add(int x,int l,int r,int L,int R) { if (l==L&&r==R) lazy[x]=(lazy[x]+1)%mo; else { b[x]=(b[x]+R-L+1)%mo; int mid=(l+r)>>1; if (L>mid) tree_add((x<<1)+1,mid+1,r,L,R); else if (R<=mid) tree_add(x<<1,l,mid,L,R); else tree_add(x<<1,l,mid,L,mid),tree_add((x<<1)+1,mid+1,r,mid+1,R); } } void tree_find(int x,int l,int r,int L,int R) { if (l==L&&r==R) sum=(sum+b[x]+1LL*lazy[x]*(r-l+1))%mo; else { int mid=(l+r)>>1; lazy[x<<1]=(lazy[x<<1]+lazy[x])%mo; lazy[(x<<1)+1]=(lazy[(x<<1)+1]+lazy[x])%mo; b[x]=(b[x]+1LL*lazy[x]*(r-l+1))%mo; lazy[x]=0; if (R<=mid) tree_find(x<<1,l,mid,L,R); else if (L>mid) tree_find((x<<1)+1,mid+1,r,L,R); else tree_find(x<<1,l,mid,L,mid),tree_find((x<<1)+1,mid+1,r,mid+1,R); } } void add(int p) { while (p!=0) { tree_add(1,1,n,w[top[p]],w[p]); p=fa[top[p]]; } } void find(int p) { while (p!=0) { tree_find(1,1,n,w[top[p]],w[p]); p=fa[top[p]]; } } int main() { n=getint(); q=getint(); for (int i=2;i<=n;i++) { p=getint()+1; fa[i]=p; link(p,i); } dep[1]=1; dfs1(1); dfs2(1,1); tot=0; for (int i=1;i<=q;i++) { u=getint()+1; v=getint()+1; p=getint()+1; a[++tot].l=u-1; a[tot].f=-1; a[tot].wei=i; a[tot].w=p; a[++tot].l=v; a[tot].f=1; a[tot].wei=i; a[tot].w=p; } sort(a+1,a+1+tot,cmp); u=0; for (int i=1;i<=tot;i++) { while (u<a[i].l) u++,add(u); sum=0; find(a[i].w); ans[a[i].wei]=(ans[a[i].wei]+1LL*sum*a[i].f+mo)%mo; } for (int i=1;i<=q;i++) printf("%lld ",ans[i]); return 0; }