jzoj5290 【NOIP2017提高组A组模拟8.17】行程的交集 (树上路径交,dfs序+树状数组维护姿势)
2017-08-17 16:29
344 查看
题面
豪哥生活在一个n个点的树形城市里面,每一天都要走来走去。虽然走的是比较的多,但是豪哥在这个城市里面的朋友并不是很多。当某一天,猴哥给他展现了一下大佬风范之后,豪哥决定要获得一些交往机会来提升交往能力。豪哥现在已经物色上了一条友,打算和它(豪哥并不让吃瓜群众知道性别)交往。豪哥现在spy了一下这个人的所有行程起点和终点,豪哥打算从终点开始走到起点与其相遇。但是豪哥是想找话题的,他想知道以前有多少次行程和此次行程是有交集的,这样豪哥就可以搭上话了。这个路径与之前路径的有交集数量作为豪哥此次的交往机会。
但是豪哥急着要做交往准备,所以算什么交往机会的小事情就交给你了。
对于另外50%的数据n,m≤200000
分析
让我们来看看树上路径交可以怎么表示。画几棵树后可以发现:令lca(a,b)=g,lca(c,d)=d。若树上路径[a,b],[c,d]有交集,则g在cd上 或 d在ab上。
这个结论并不是那么好发现(至少我没发现),可以粗略的感性理解一下:
假设我们现在手上是一条lca深度大的路径,这条路径最高可达点就是lca,若lca不在另一条路径上,又因为他lca深度大,所以不可能在另外一条路径lca上方,所以就没有可达点了。
记住这个姿势,以后会用到。
那么问题就变成了,在ab上找之前路径的lca 与 在找覆盖lca的之前路径。
整体地看,这两种情况是没有重合的,因为若ab的lca在cd路径上,则ab不可能经过cd的lca.
极端情况,容易发现当lca相等的时候需要特判。
Part1: ab上找之前路径的lca
静态树考虑dfs序。 (动态树考虑欧拉序)对于一个点x,a->lca(a,b)这样的树链如果经过他,则一定是从它的子树走向它的祖先。
然后就比较显然了,每多加一个这样的x,则给他子树中所有点的权值都+1.
查询(a,lca(a,b),b)的时候,就用a权+b权-lca权*2.
若a与lca在x的上下两方,则会被累加到贡献。
若同时在x的下方,则会被lca权减掉。
Part2: 覆盖lca的之前路径
同样地考虑dfs序,很方便的树上前缀和。若有一条路径(a,b),则给lca(a,b) +1,a-1,b-1.然后查询一个点到根的权值和,这是离线时的套路。
但这题这样不好做,因为不能每一次都重构树,于是我们变一下,变成类似树上后缀和的东西。
对于一条路径(a,b),给a+1,b+1,lca-2.
一个点的子树权值和就是他的权值。
用两树状数组维护一下dfs序上的信息,我们就把这道题给搞定了。
这题的坑点主要在于,两个树状数组维护的信息意义是不一样的,压根不可以相提并论。
Demo
#include <cstdio> #include <iostream> #define lowbit(x) ((x)&-(x)) #define treeSum(t,x) (sum(t,R[x])-sum(t,L[x]-1)) using namespace std; const int N=2e5+10; int n,m,L ,R ,stm; int tot,to[N*2],next[N*2],final ,app ; int f [19],dep ; int t1 ,t2 ,ans; void change(int *tr,int x,int v) {for (; x<=n; x+=lowbit(x)) tr[x]+=v;} int sum(int *tr,int x) {int ret=0; for (; x; x-=lowbit(x)) ret+=tr[x]; return ret;} void link(int x,int y) { to[++tot]=y,next[tot]=final[x],final[x]=tot; } void dfs(int x,int fa) { L[x]=++stm; f[x][0]=fa; for (int i=1; i<19; i++) f[x][i]=f[f[x][i-1]][i-1]; dep[x]=dep[fa]+1; for (int i=final[x]; i; i=next[i]) if (to[i]!=fa) dfs(to[i],x); R[x]=stm; } int lca(int x,int y) { if (dep[x]<dep[y]) swap(x,y); for (int i=18; i>=0; i--) if (dep[f[x][i]]>=dep[y]) x=f[x][i]; if (x==y) return x; for (int i=18; i>=0; i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } void calcAns(int x,int y,int lca) { ans+=treeSum(t1,lca); ans+=sum(t2,L[x])+sum(t2,L[y])-sum(t2,L[lca])*2; } int main() { freopen("3.in","r",stdin); cin>>n; for (int i=1; i<n; i++) { int u,v; scanf("%d %d\n",&u,&v); link(u,v),link(v,u); } dfs(1,0); cin>>m; for (int i=1; i<=m; i++) { int u,v; scanf("%d %d\n",&u,&v); int g=lca(u,v); ans=0; calcAns(u,v,g); printf("%d\n",ans+app[g]); app[g]++; change(t1,L[u],1); change(t1,L[v],1); change(t1,L[g],-2); change(t2,L[g],1); change(t2,R[g]+1,-1); } }
相关文章推荐
- 【jzoj5290】【NOIP2017提高组A组模拟8.17】【行程的交集】
- jzoj5336 【NOIP2017提高A组模拟8.24】提米树 (dfs序dp,奇异姿势dp)
- JZOJ5385. 【NOIP2017提高A组模拟9.23】Carry 树上倍增
- 【jzoj5289】【NOIP2017提高组A组模拟8.17】【偷笑】【数据结构】
- 【JZOJ4715】【NOIP2016提高A组模拟8.19】树上路径
- 【jzoj5288】【NOIP2017提高组A组模拟8.17】【球场大佬】
- JZOJ 4715 【NOIP2016提高A组模拟8.19】树上路径
- JZOJ5394. 【NOIP2017提高A组模拟10.5】Ping 树上差分 树状数组
- JZOJ 5177. 【NOIP2017提高组模拟6.28】TRAVEL
- JZOJ 5328. 【NOIP2017提高A组模拟8.22】世界线
- 【JZOJ5330】【NOIP2017提高A组模拟8.22】密码【51nod1569】二项式系数的个数
- JZOJ 100026. 【NOIP2017提高A组模拟7.7】图
- jzoj. 100031. 【NOIP2017提高A组模拟7.9】外星密码
- 【JZOJ4710】【NOIP2016提高A组模拟8.17】Value
- [JZOJ5395]【NOIP2017提高A组模拟10.6】Count
- JZOJ 5399. 【NOIP2017提高A组模拟10.7】Confess
- JZOJ5399. 【NOIP2017提高A组模拟10.7】Confess bitset
- JZOJ 5404. 【NOIP2017提高A组模拟10.10】Graph
- JZOJ 5305. 【NOIP2017提高A组模拟8.18】C
- 【JZOJ4920】【NOIP2017提高组模拟12.10】降雷皇