给定n个节点的有根树,q次询问,每次询问求$\sum _{l\leq i\leq r} dep[LCA(i,z)] $
˼·
根据wys巨神所说,如果不把dep这个约束去掉,那么将不容易用数据结构来维护,因为对于不同的i,\(dep[LCA]\)可能不一样
1.为了去掉dep,我们采取一种奇技淫巧,对于上面的一个 \(i\) ,将 \(root\) 到 \(i\) 这条路径上的所有点权加一,那么\(dep[LCA]\)就可以通过查询\(root\)到\(z\)这条路径上的点权和得到(WTF???)
于是问题变成了链修改与链查询,可以请出我们代码简短好写的树链剖分同学
然而我们不能对每个询问都暴力修改\(root\)到\([l,r]\)这一些链,所以我们还需要一些小优化
2.离线处理,将每个询问拆成\([1,l-1]\)和\([1,r]\)两份,那么这个询问可以用\(ans[1,r]-ans[1,l-1]\)得到,由于链修改操作与z无关(因为增加\(root\)到\(i\)所以只与\(i\)有关),所以可以对所有询问的右端点排序,然后for一遍依次链修改即可
树链剖分模板题
由于有很多重复的操作(+代码过丑)所以去掉不必要的部分啦~~~
Code:
struct Q { int r,z,opt; ll ans; bool operator < (const Q a)const { return r<a.r; } }q[N<<1];int qsum=-1; bool cmp(Q a,Q b) {return a.opt<b.opt;} int main() { read(n);read(m); for(int i=1;i<n;++i) { int father; read(father); add_edge(father+1,i+1); } for(int i=1;i<=m;++i) { int l,r,z; read(l);read(r);read(z); ++l;++r;++z; q[++qsum].r=l-1; q[qsum].z=z; q[qsum].opt=qsum; q[++qsum].r=r; q[qsum].z=z; q[qsum].opt=qsum; } sort(q,q+qsum+1); seg[1]=rev[1]=top[1]=hfu=1; dfs1(1); dfs2(1); int now=0; for(int i=0;i<=qsum;++i) { while(now<q[i].r) modify_edge(1,++now,1); q[i].ans=query_edge(1,q[i].z); } sort(q,q+qsum+1,cmp); for(int i=1;i<=qsum;i+=2) printf("%lld\n",((q[i].ans-q[i-1].ans)%mod+mod)%mod); return 0; }