随笔 - 58,  文章 - 0,  评论 - 4,  阅读 - 3296

 一、题目描述:

  已知一棵包含 n (n<=1e5) 个结点的树,连通且无环,每个节点上包含一个数值,需要支持以下操作:

  <1>: 1 x y z,表示将树从 x 到 y 结点最短路径上所有节点的值都加上 z 。

  <2>: 2 x y,表示求树从 x 到 y 结点最短路径上所有节点的值之和。

  <3>: 3 x z,表示将以 x 为根节点的子树内所有节点值都加上 z 。

  <4>: 4 x,表示求以 x 为根节点的子树内所有节点值之和。


 二、解题思路:

  板子题,不需要思路。时间复杂度 O(nlog2n) 。


 三、完整代码:

复制代码
  1 #include<iostream>
  2 #include<cstring>
  3 #define N 100010
  4 #define ls(p) p<<1
  5 #define rs(p) p<<1|1
  6 #define ll long long
  7 using namespace std;
  8 ll val[N],out[N],tag[N*4];
  9 ll fa[N],du[N],top[N],size[N];
 10 ll f[N],son[N],sum[N*4],rak[N];
 11 ll n,q,s,mod,u1,v1,tot,opt,x,y,z;
 12 struct EDGE{
 13     ll v;
 14     ll nxt;
 15 }edge[N*2];
 16 ll head[N],cnt;
 17 void add(ll u,ll v)
 18 {
 19     edge[++cnt].v=v;
 20     edge[cnt].nxt=head[u];
 21     head[u]=cnt;
 22 }
 23 void dfs1(ll now,ll d)//dfs1
 24 {
 25     du[now]=d;size[now]=1;
 26     for(ll i=head[now];i!=-1;i=edge[i].nxt)
 27         if(!du[edge[i].v])
 28         {
 29             fa[edge[i].v]=now;
 30             dfs1(edge[i].v,d+1);
 31             size[now]+=size[edge[i].v];
 32             if(size[edge[i].v]>size[son[now]])
 33                 son[now]=edge[i].v;
 34         }
 35 }
 36 void dfs2(ll now,ll t)//dfs2 
 37 {
 38     top[now]=t;
 39     f[now]=++tot;
 40     rak[f[now]]=now;
 41     if(!son[now])
 42     {
 43         out[now]=tot;
 44         return ;
 45     }
 46     dfs2(son[now],t);
 47     for(ll i=head[now];i!=-1;i=edge[i].nxt)
 48         if(edge[i].v!=fa[now]&&edge[i].v!=son[now])
 49             dfs2(edge[i].v,edge[i].v);
 50     out[now]=tot;
 51 }
 52 void push_up(ll p)//保证p有儿子 
 53 {
 54     sum[p]=sum[ls(p)];//可能出错 
 55     sum[p]+=sum[rs(p)];
 56 }
 57 void push_down(ll p,ll l,ll r)
 58 {
 59     ll mid=(l+r)>>1;
 60     tag[ls(p)]+=tag[p];
 61     tag[rs(p)]+=tag[p];
 62     sum[rs(p)]+=(r-mid)*tag[p];
 63     sum[ls(p)]+=(mid-l+1)*tag[p];tag[p]=0;
 64 }
 65 void build(ll p,ll l,ll r)
 66 {
 67     if(l==r)
 68     {
 69         sum[p]=val[rak[l]];
 70         return ;
 71     }
 72     ll mid=(l+r)>>1;
 73     build(ls(p),l,mid);
 74     build(rs(p),mid+1,r);
 75     push_up(p);
 76 }
 77 void update(ll p,ll l,ll r,ll nl,ll nr,ll k)
 78 {
 79     if(nl<=l&&r<=nr)
 80     {
 81         tag[p]+=k;
 82         sum[p]+=(r-l+1)*k;
 83         return ;
 84     }
 85     push_down(p,l,r);ll mid=(l+r)>>1;
 86     if(nl<=mid)    update(ls(p),l,mid,nl,nr,k);
 87     if(nr>mid)    update(rs(p),mid+1,r,nl,nr,k);
 88     push_up(p);
 89 }
 90 void upd(ll u,ll v,ll z)
 91 {
 92     ll tu=top[u],tv=top[v];
 93     while(tu!=tv)
 94     {
 95         if(du[tu]<du[tv])    swap(u,v),swap(tu,tv);
 96         update(1,1,n,f[tu],f[u],z); 
 97         u=fa[tu],tu=top[u]; 
 98     }
 99     if(du[u]<du[v])    swap(u,v);
100     update(1,1,n,f[v],f[u],z);
101 }//有点难,画个图就理解了(注意重链剖分的特性) 
102 ll query(ll p,ll l,ll r,ll nl,ll nr)
103 {
104     if(nl<=l&&r<=nr)
105         return sum[p];
106     push_down(p,l,r);
107     ll mid=(l+r)>>1,ans=0;
108     if(nl<=mid)    ans+=query(ls(p),l,mid,nl,nr);
109     if(nr>mid)    ans+=query(rs(p),mid+1,r,nl,nr);
110     return ans;
111 }
112 ll que(ll u,ll v)
113 {
114     ll tu=top[u],tv=top[v],ans=0;
115     while(tu!=tv)
116     {
117         if(du[tu]<du[tv])    swap(u,v),swap(tu,tv);
118         ans+=query(1,1,n,f[tu],f[u]);
119         u=fa[tu],tu=top[u];
120     }
121     if(du[u]<du[v])    swap(u,v);
122     ans+=query(1,1,n,f[v],f[u]);
123     return ans;
124 }
125 int main()
126 {
127     ios::sync_with_stdio(false);
128     cin.tie(0);cout.tie(0);
129     cin>>n>>q>>s>>mod;
130     for(ll i=1;i<=n;i++)
131         cin>>val[i];
132     memset(head,-1,sizeof(head));
133     for(ll i=1;i<n;i++)
134     {
135         cin>>u1>>v1;
136         add(u1,v1);
137         add(v1,u1);
138     }
139     dfs1(s,1);dfs2(s,s);build(1,1,n);
140     for(ll i=1;i<=q;i++)
141     {
142         cin>>opt;
143         if(opt==1)
144         {
145             cin>>x>>y>>z;
146             upd(x,y,z);
147         }
148         if(opt==2)
149         {
150             cin>>x>>y;
151             cout<<que(x,y)%mod<<'\n';
152         }
153         if(opt==3)
154         {
155             cin>>x>>z;
156             update(1,1,n,f[x],out[x],z);
157         }
158         if(opt==4)
159         {
160             cin>>x;
161             cout<<query(1,1,n,f[x],out[x])%mod<<'\n';
162         }
163     }
164     return 0;
165 }
复制代码

四、写题心得:

  今天下午老师讲了树链剖分,听的很明白,但是这个模板直接写了我一个下午qwq。

  够长!(然而我自认为已经写得很短了)。下面说一下细节:

  <1>:注意取模(这次被这个坑了好久)

  <2>:两个dfs注意函数名(这个其实这次做的还不错)

  <3>:有些时候为了准确性可以放弃美观(例如第54行)

  <4>:线段树 update 和 query 时都要 push_down

  <5>:build 线段树时记得要转换为 dfn 序(求rak数组)

  <6>:第100行个人觉得不是很好理解(建议画个图)

  <7>:out[x]已经是 dfn 序,不要再套一个 dfn 数组 !(又被坑了!)

  写完了,拜拜!

posted on   trh0630  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示