[关闭]
@xiaoziyao 2020-08-04T16:13:41.000000Z 字数 1427 阅读 1040

CF383C Propagating tree解题报告

解题报告


CF383C Propagating tree解题报告:

更好的阅读体验

题意

题意:给你一颗个结点带点权的树,支持两个操作:①把某个点权值加,它的所有儿子权值加,它所有儿子的儿子权值加……②求某点权值
数据范围:

分析

这是一道比较好的dfn序练习题。

dfn序,即某个点在dfs时第一次遍历的时间戳,有一个很优秀的性质:某个点的子树(包括它自己)中所有点的dfn序恰好是一段连续的序列。这个性质很显然,用dfs的搜索顺序想就很容易理解了。

看到这道题,我们发现它的修改都是关于子树的,所以我们可以想到用dfn序把树拍平成序列来维护。

但是它还有一个问题,如何让它的子树间断地加减权值呢?其实这也很简单,这个操作等价于这个点所有级儿子加,所有级儿子减,所有级儿子加……因为dfs的顺序,所以我们可以知道dfn序的另一个性质——若两个点层数奇偶性不同,那么它们的dfn序奇偶性也不同。

这样就好办了,由于是区间修改,单点查询,所以可以建两个树状数组,一个维护奇数层次,一个维护偶数层次,然后修改差分一下,查询直接求就可以了。

代码

  1. #include<stdio.h>
  2. #define lowbit(x) x&-x
  3. const int maxn=200005,maxm=400005,maxt=400005;
  4. int i,j,k,m,n,e,cnt;
  5. int start[maxn],to[maxm],then[maxm],sum[maxt][2],dfn[maxn],end[maxn],tp[maxn],a[maxn];
  6. inline void add(int x,int y){
  7. then[++e]=start[x],start[x]=e,to[e]=y;
  8. }
  9. void update(int x,int v,int t){
  10. for(int i=x;i<maxt;i+=lowbit(i))
  11. sum[i][t]+=v;
  12. }
  13. int query(int x,int t){
  14. int res=0;
  15. for(int i=x;i;i-=lowbit(i))
  16. res+=sum[i][t];
  17. return res;
  18. }
  19. void dfs(int x,int last,int t){
  20. dfn[x]=++cnt,tp[x]=t;
  21. for(int i=start[x];i;i=then[i]){
  22. int y=to[i];
  23. if(y==last)
  24. continue;
  25. dfs(y,x,t^1);
  26. }
  27. end[x]=cnt;
  28. }
  29. int main(){
  30. scanf("%d%d",&n,&m);
  31. for(i=1;i<=n;i++)
  32. scanf("%d",&a[i]);
  33. for(i=1;i<n;i++){
  34. int x,y;
  35. scanf("%d%d",&x,&y);
  36. add(x,y),add(y,x);
  37. }
  38. dfs(1,0,0);
  39. for(i=1;i<=m;i++){
  40. int opt,x,v;
  41. scanf("%d",&opt);
  42. if(opt==1){
  43. scanf("%d%d",&x,&v);
  44. update(dfn[x],v,tp[x]);
  45. update(end[x]+1,-v,tp[x]);
  46. }
  47. if(opt==2){
  48. scanf("%d",&x);
  49. printf("%d\n",a[x]+query(dfn[x],tp[x])-query(dfn[x],tp[x]^1));
  50. }
  51. }
  52. return 0;
  53. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注