BZOJ 3626: [LNOI2014]LCA 树链剖分 线段树 离线
http://www.lydsy.com/JudgeOnline/problem.php?id=3626
LNOI的树链剖分题没有HAOI那么水,学到的东西还是很多的。
我如果现场写,很难想出来这种题,是时候复习一波离线算法泡脑子了。(没有暴力分的题,想不出来正解就爆零,太可怕了)
排序后离线操作通过前缀和计算答案,题解是hzwer的博客上复制的
直接引用清华爷gconeice的题解吧
显然,暴力求解的复杂度是无法承受的。
考虑这样的一种暴力,我们把 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)· log n · log n),LCT的复杂度为 O((n + q)· log n),均可以完成任务。至此,题目已经被我们完美解决。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 const int maxn=50100; 8 const long long modn=1000000007; 9 const long long minf=1<<30; 10 int n,m; 11 struct nod{ 12 int y,next; 13 }e[maxn];int head[maxn]={},tot=0; 14 int fa[maxn]={},top[maxn]={},pos[maxn]={},kid[maxn]={},dep[maxn]={}; 15 void init(int x,int y){ 16 e[++tot].y=y;e[tot].next=head[x];head[x]=tot; 17 } 18 int dfs1(int x){ 19 int y,hug=0,siz,tsn=1;dep[x]=dep[fa[x]]+1; 20 for(int i=head[x];i;i=e[i].next){ 21 y=e[i].y; 22 if(y==fa[x])continue; 23 siz=dfs1(y); 24 if(siz>hug)hug=siz,kid[x]=y; 25 tsn+=siz; 26 }return tsn; 27 } 28 void dfs2(int x,int pa){ 29 int y;top[x]=pa;pos[x]=++tot; 30 if(kid[x])dfs2(kid[x],pa); 31 for(int i=head[x];i;i=e[i].next){ 32 y=e[i].y; 33 if(y==kid[x]||y==fa[x])continue; 34 dfs2(y,y); 35 } 36 } 37 struct seg{ 38 long long sum,w,l,r; 39 seg(){sum=l=r=0;} 40 }t[maxn*4]; 41 void build(int x,int l,int r){ 42 t[x].l=l;t[x].r=r; 43 if(l==r)return; 44 int mid=(l+r)/2; 45 build(x*2,l,mid); 46 build(x*2+1,mid+1,r); 47 } 48 void pushup(int x){ 49 if(t[x].r>t[x].l)t[x].sum=t[x*2].sum+t[x*2+1].sum; 50 t[x].sum+=(t[x].r-t[x].l+1)*t[x].w; 51 } 52 void add(int x,int l,int r){ 53 if(l<=t[x].l&&t[x].r<=r){ 54 if(t[x].l==t[x].r)t[x].sum+=1; 55 else {t[x].w+=1;pushup(x);} 56 return; 57 } 58 int mid=(t[x].l+t[x].r)/2,ls=x*2,rs=x*2+1; 59 if(r>mid) add(rs,l,r); 60 if(l<=mid) add(ls,l,r); 61 pushup(x); 62 } 63 long long sum(int x,int l,int r,int w){ 64 if(l<=t[x].l&&t[x].r<=r)return t[x].sum+w*(t[x].r-t[x].l+1); 65 int mid=(t[x].l+t[x].r)/2,ls=x*2,rs=x*2+1;long long tsn=0; 66 if(l<=mid) tsn+=sum(ls,l,r,w+t[x].w); 67 if(r>mid) tsn+=sum(rs,l,r,w+t[x].w); 68 return tsn; 69 } 70 long long doit(int x){ 71 int a=top[x];long long tsn=0; 72 for(;a!=1;){ 73 tsn+=sum(1,pos[a],pos[x],0); 74 x=fa[a];a=top[x]; 75 } 76 tsn+=sum(1,pos[a],pos[x],0); 77 return tsn; 78 } 79 void datup(int x){ 80 int a=top[x]; 81 for(;a!=1;){ 82 add(1,pos[a],pos[x]); 83 x=fa[a];a=top[x]; 84 } 85 add(1,pos[a],pos[x]); 86 } 87 struct lcc{ 88 int num; 89 int z;int id; 90 long long ans; 91 }q[maxn*2]; 92 bool cmp1(lcc aa,lcc bb){return aa.num<bb.num;} 93 bool cmp2(lcc aa,lcc bb){return aa.id<bb.id;} 94 int main(){ 95 scanf("%d%d",&n,&m); 96 int x,y,z; 97 for(int i=1;i<n;i++){ 98 scanf("%d",&y); 99 init(y+1,i+1); 100 fa[i+1]=y+1; 101 }tot=0;dfs1(1);dfs2(1,1);build(1,1,n);tot=0; 102 for(int j=1;j<=m;j++){ 103 scanf("%d%d%d",&x,&y,&z); 104 x++;y++;z++; 105 if(y<x)swap(x,y); 106 q[++tot].num=x-1;q[tot].z=z;q[tot].id=tot; 107 q[++tot].num=y;q[tot].z=z;q[tot].id=tot; 108 }sort(q+1,q+1+tot,cmp1); 109 int now=0; 110 for(int i=1;i<=tot;i++){ 111 while(now<q[i].num){ 112 now++;datup(now); 113 } 114 q[i].ans=doit(q[i].z); 115 } 116 sort(q+1,q+1+tot,cmp2); 117 for(int i=1;i<=m;i++){ 118 long long z=(q[i*2].ans-q[i*2-1].ans)%201314; 119 printf("%lld\n",z); 120 } 121 return 0; 122 }