BZOJ3626: [LNOI2014]LCA
Description
给出一个n个节点的有根树(编号为0到n-1,根节点为0)。
一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)
Input
第一行2个整数n q。
接下来n-1行,分别表示点1到点n-1的父节点编号。
接下来q行,每行3个整数l r z。
Output
输出q行,每行表示一个询问的答案。每个答案对201314取模输出
Sample Input
0
0
1
1
1 4 3
1 4 2
Sample Output
5
HINT
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。
题解Here!
又是树上的询问,首选 树链剖分/LCT。
这题就是树链剖分+线段树+差分。
deep[i]是什么?——就是从 i 点到根有多少个点(包括 i )。
我们从整体上考虑,发现对于一个询问:l , r , z 来说,所有的 lca 都在 z 到根的路径上。
从而有一些点,它们对很多的 lca 的深度都有贡献,而这个贡献等于在这个点下面的 lca 的个数,所以我们可以把每个 lca 到根的路径上的每个点的权值都加一。
然后从 z 向上走到根,沿路统计的权值就是答案了。
就是:对于一个询问: l , r , z ,我们把每个点 i ( l <= i <= r ) 到根的路径上的每一个点的权值都加一,因为:
所有的 lca 都在 z 到根的路径上,所以我们可以从 z 点向上爬到根,沿途统计的点权值之和,就是答案了。
我们每次的操作都是从某个点到根的,所以树链剖分+线段树就好了。
但是我们每次清空线段树,然后从 l ~ r 再添加一遍,树剖+线段树的复杂度就是 n*(logn)^2 的,还要做 q 次,复杂度依然不理想。
于是想到:差分可以将询问拆开,而且每个拆开的区间之间是有重叠的,是可以转移的,而不用每次都清空。
而且差分后的数组只与右端点有关!
所以我们可以将差分后的区间按照右端点从小到大排序(左端点都是根),然后按从小到大的顺序添加点,每遇到一个询问就查询一次。
于是一波sao操作就A了。。。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #define LSON rt<<1 #define RSON rt<<1|1 #define DATA(x) b[x].data #define SIGN(x) b[x].c #define LSIDE(x) b[x].l #define RSIDE(x) b[x].r #define WIDTH(x) (RSIDE(x)-LSIDE(x)+1) #define MAXN 50010 #define MOD 201314 using namespace std; int n,m,c=1,d=1,e=1; int head[MAXN],deep[MAXN],fa[MAXN],son[MAXN],size[MAXN],id[MAXN],top[MAXN]; struct node1{ int next,to; }a[MAXN]; struct node2{ int data,c,l,r; }b[MAXN<<2]; struct node3{ int x,u,id; bool flag; }que[MAXN<<1]; struct node4{ int l,r; }ans[MAXN]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } bool cmp(const node3 &x,const node3 &y){ if(x.x==y.x)return x.id<y.id; return x.x<y.x; } inline void add(int x,int y){ a[c].to=y; a[c].next=head[x]; head[x]=c++; } inline void addque(int l,int r,int u,int i){ que[e].x=l;que[e].u=u;que[e].id=i; que[e++].flag=false; que[e].x=r;que[e].u=u;que[e].id=i; que[e++].flag=true; } void dfs1(int rt){ son[rt]=0;size[rt]=1; for(int i=head[rt];i;i=a[i].next){ int will=a[i].to; if(!deep[will]){ deep[will]=deep[rt]+1; fa[will]=rt; dfs1(will); size[rt]+=size[will]; if(size[son[rt]]<size[will])son[rt]=will; } } } void dfs2(int rt,int f){ id[rt]=d++;top[rt]=f; if(son[rt])dfs2(son[rt],f); for(int i=head[rt];i;i=a[i].next){ int will=a[i].to; if(will!=fa[rt]&&will!=son[rt]) dfs2(will,will); } } inline void pushup(int rt){ DATA(rt)=(DATA(LSON)+DATA(RSON))%MOD; } inline void pushdown(int rt){ if(!SIGN(rt)||LSIDE(rt)==RSIDE(rt))return; SIGN(LSON)=(SIGN(LSON)+SIGN(rt))%MOD; DATA(LSON)=(DATA(LSON)+SIGN(rt)*WIDTH(LSON))%MOD; SIGN(RSON)=(SIGN(RSON)+SIGN(rt))%MOD; DATA(RSON)=(DATA(RSON)+SIGN(rt)*WIDTH(RSON))%MOD; SIGN(rt)=0; } void buildtree(int l,int r,int rt){ int mid; LSIDE(rt)=l; RSIDE(rt)=r; if(l==r){ DATA(rt)=0; return; } mid=l+r>>1; buildtree(l,mid,LSON); buildtree(mid+1,r,RSON); pushup(rt); } void update(int l,int r,int c,int rt){ int mid; if(l<=LSIDE(rt)&&RSIDE(rt)<=r){ SIGN(rt)=(SIGN(rt)+c)%MOD; DATA(rt)=(DATA(rt)+c*WIDTH(rt))%MOD; return; } pushdown(rt); mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)update(l,r,c,LSON); if(mid<r)update(l,r,c,RSON); pushup(rt); } int query(int l,int r,int rt){ int mid,ans=0; if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt); pushdown(rt); mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)ans=(ans+query(l,r,LSON)%MOD)%MOD; if(mid<r)ans=(ans+query(l,r,RSON)%MOD)%MOD; return ans%MOD; } void work_update(int x,int y,int k){ while(top[x]!=top[y]){ if(deep[top[x]]<deep[top[y]])swap(x,y); update(id[top[x]],id[x],1,1); x=fa[top[x]]; } if(deep[x]>deep[y])swap(x,y); update(id[x],id[y],1,1); return; } int work_query(int x,int y){ int s=0; while(top[x]!=top[y]){ if(deep[top[x]]<deep[top[y]])swap(x,y); s=(s+query(id[top[x]],id[x],1)%MOD)%MOD; x=fa[top[x]]; } if(deep[x]>deep[y])swap(x,y); s=(s+query(id[x],id[y],1)%MOD)%MOD; return s; } void work(){ int now=1,id; for(int i=1;i<e;i++){ while(now<=que[i].x){ work_update(1,now,1); now++; } id=que[i].id; if(que[i].flag)ans[id].r=work_query(1,que[i].u); else ans[id].l=work_query(1,que[i].u); } for(int i=1;i<=m;i++)printf("%d\n",(ans[i].r-ans[i].l+MOD)%MOD); } void init(){ int x,l,r,u; n=read();m=read(); for(int i=2;i<=n;i++){ x=read()+1; add(x,i); } deep[1]=1; dfs1(1); dfs2(1,1); buildtree(1,n,1); for(int i=1;i<=m;i++){ l=read();r=read()+1;u=read()+1; addque(l,r,u,i); } sort(que+1,que+e,cmp); } int main(){ init(); work(); return 0; }