Ri_Nai ♥3

题目大意

给出一棵以1为根的有根树,每个非叶子节点都有一条到其儿子节点的有向边.每个非根节点都有一条到根的返祖边.有 q q 个询问,每次修改一条边的权值或者查询树上两点间的最短路

树上距离


按照题目大意建出一棵树
因为所有的树边都是有向边,所以树上的距离有如下性质
当且仅当u为v的祖先时u->v才有树上的路径
记树上距离(u,v) d i s ( u , v ) dis(u,v)

中转站:根

每一个点都有一条到根的返祖边,设 u u 的返祖边长度为 l e n ( u ) len(u)
因为根是所有点的祖先,以根为中转站, u v u\Rightarrow v 的路径可以改写成 u 1 v u\Rightarrow 1 \Rightarrow v
此时距离为 l e n ( u ) + d i s ( 1 , u ) len(u)+dis(1,u)
可以证明,中间只会跳到根节点一次.
否则距离则为 l e n ( u ) + d i s ( 1 , p ) + l e n ( p ) + d i s ( 1 , u ) < l e n ( u ) + d i s ( 1 , u ) len(u)+dis(1,p)+len(p)+dis(1,u)<len(u)+dis(1,u)

中转站:子树

u u 是其子树所有点的祖先,故 u u 可以到达其子树中的点 p p .
故此时 u v u\Rightarrow v 的距离为 d i s ( u , p ) + l e n ( p ) + d i s ( 1 , v ) dis(u,p)+len(p)+dis(1,v)

最短路

u u v v 的祖先,则答案很明了
就是 d i s ( u , v ) dis(u,v) ,因为 d i s ( u , v ) < = d i s ( 1 , v ) < l e n ( p ) + d i s ( 1 , v ) ( p u 的子树内 ) dis(u,v)<=dis(1,v)<len(p)+dis(1,v)(p在u的子树内)
否则 u v u\Rightarrow v 的距离为
r e s = m i n ( d i s ( u , p ) + l e n ( p ) + d i s ( 1 , v ) ) res=min (dis(u,p)+len(p)+dis(1,v))
r e s = m i n ( d i s ( 1 , p ) + l e n ( p ) ) d i s ( 1 , u ) + d i s ( 1 , v ) res=min (dis(1,p)+len(p))-dis(1,u)+dis(1,v)
d i s ( 1 , u ) dis(1,u) d i s ( 1 , v ) dis(1,v) 在查询的时候是固定的
故方向很明了了,只需要求 u u 子树内 m i n ( d i s ( 1 , p ) + l e n ( p ) ) min(dis(1,p)+len(p))
由于是区间/单点更新权值,而区间查询的是最值,故用线段树维护比较方便

维护&查询

线段树需要的功能

1.在修改树边的时候把整个子树的权值加上更改的权值
2.在修改返祖边的时候把一个点的权值加上更改的权值
3.区间查询整个区间的 d i s ( 1 , p ) + l e n ( p ) dis(1,p)+len(p)
4.单点查询某个点 p p d i s ( 1 , p ) + l e n ( p ) l e n ( p ) dis(1,p)+len(p)-len(p)

解决的是子树的问题,故用dfs序维护即可,不需树链剖分
其次,判断 u u v v 是否为祖先关系的时候也可以用dfs序
v v 的dfs序在 u u 的子树的dfs序区间内,则 u u v v 的祖先
L [ u ] < L [ v ]   a n d   L [ v ] < = R [ u ] L[u]<L[v]\ and\ L[v]<=R[u]
由于这道题比较阴间,可能出现 u = = v u==v 的情况
所以这个判断是 L [ u ] < = L [ v ]   a n d   L [ v ] < = R [ u ] L[u]<=L[v]\ and\ L[v]<=R[u]

代码

把自己考场代码优化了一下,因为原本求 d i s ( 1 , u ) dis(1,u) 的时候用的是树状数组(因为感觉写起来比线段树方便)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=2e5+5,M=N<<1;
  4. typedef long long LL;
  5. int n,Q,fa[N],dep[N],len[N],L[N],R[N],Re[N];
  6. int nxt[N],to[N],head[N],val[N],tot,U[M],V[M],W[M];
  7. LL dis[N];
  8. void Add(int u,int v,int w)
  9. {
  10. nxt[++tot]=head[u];
  11. to[head[u]=tot]=v;
  12. val[tot]=w;
  13. }
  14. void dfs(int u,int f)
  15. {
  16. dep[Re[L[u]=++tot]=u]=dep[fa[u]=f]+1;
  17. for(int i=head[u];i;i=nxt[i])
  18. {
  19. int v=to[i];
  20. if(v==f)continue;
  21. dis[v]=dis[u]+val[i];
  22. dfs(v,u);
  23. }R[u]=tot;
  24. }
  25. struct Seg
  26. {
  27. LL Sum[N<<2],lz[N<<2];
  28. #define ls p<<1
  29. #define rs p<<1|1
  30. #define Lson L,mid,ls
  31. #define Rson mid+1,R,rs
  32. #define LRP int L=1,int R=n,int p=1
  33. //个人打线段树常用的define
  34. void Chg(int p,int x)//修改
  35. {
  36. Sum[p]+=x,lz[p]+=x;
  37. }
  38. void Down(int p)//延迟更新
  39. {
  40. LL &t=lz[p];
  41. if(!t)return ;
  42. Chg(ls,t);
  43. Chg(rs,t);
  44. t=0;
  45. }
  46. void Updata(int l,int r,int x,LRP)//区间更新
  47. {
  48. if(L==l&&r==R)return Chg(p,x);
  49. Down(p);int mid=L+R>>1;
  50. if(r<=mid)Updata(l,r,x,Lson);
  51. else if(l>mid)Updata(l,r,x,Rson);
  52. else Updata(l,mid,x,Lson),Updata(mid+1,r,x,Rson);
  53. Sum[p]=min(Sum[ls],Sum[rs]);
  54. }
  55. void Updata_(int x,int y,LRP)//单点更新
  56. {
  57. if(L==R)return Sum[p]+=y,void();
  58. Down(p);int mid=L+R>>1;
  59. if(x<=mid)Updata_(x,y,Lson);
  60. else Updata_(x,y,Rson);
  61. Sum[p]=min(Sum[ls],Sum[rs]);
  62. }
  63. void Build(LRP)//建线段树
  64. {
  65. if(L==R)
  66. {
  67. int u=Re[L];
  68. Sum[p]=dis[u]+len[u];
  69. return ;
  70. }
  71. int mid=L+R>>1;
  72. Build(Lson),Build(Rson);
  73. Sum[p]=min(Sum[ls],Sum[rs]);
  74. }
  75. LL Query(int l,int r,LRP)//区间查询
  76. {
  77. if(L==l&&r==R)return Sum[p];
  78. Down(p);int mid=L+R>>1;
  79. if(r<=mid)return Query(l,r,Lson);
  80. if(l>mid)return Query(l,r,Rson);
  81. return min(Query(l,mid,Lson),Query(mid+1,r,Rson));
  82. }
  83. LL Query_(int x,LRP)//单点查询
  84. {
  85. if(L==R)return Sum[p]-len[Re[L]];
  86. Down(p);int mid=L+R>>1;
  87. if(x<=mid)return Query_(x,Lson);
  88. return Query_(x,Rson);
  89. }
  90. }T;
  91. int main()
  92. {
  93. cin>>n>>Q;
  94. for(int i=1;i<n;++i)
  95. scanf("%d%d%d",&U[i],&V[i],&W[i]),
  96. Add(U[i],V[i],W[i]),swap(U[i],V[i]);
  97. dfs(1,tot=0);
  98. for(int i=n;i<=n+n-2;++i)
  99. scanf("%d%d%d",&U[i],&V[i],&W[i]),len[U[i]]=W[i];
  100. T.Build();
  101. for(int op,u,v;Q--;)
  102. {
  103. scanf("%d%d%d",&op,&u,&v);
  104. if(op==1)
  105. {
  106. int p=U[u];
  107. if(u<n)T.Updata(L[p],R[p],v-W[u]);
  108. else T.Updata_(L[p],v-W[u]),len[p]=v;
  109. W[u]=v;
  110. }
  111. else
  112. if(L[u]<=L[v]&&L[v]<=R[u])//v是u的后代
  113. printf("%lld\n",T.Query_(L[v])-T.Query_(L[u]));
  114. else//否则求区间最小值
  115. printf("%lld\n",T.Query(L[u],R[u])-T.Query_(L[u])+T.Query_(L[v]));
  116. }
  117. }
点赞