树链剖分 (ZQU1607)

这道题与模板之间,多了个确定哪个为根的操作;

这道题有技巧,并不需要真正去建立以某个节点为根的树

关于路径的操作,无论以哪个点为根,得出的答案无影响;

关于对子节点进行操作的,有几种情况,

当查询节点刚好是根节点的话,就直接从1开始遍历就好 (因为这道题是以1为根节点)

当查询的节点的孩子或孙子中包括根节点的话,则需要用根节点得出的值去剪掉这个根节点得出的值 

(这里以查询作为例子,更新值也是同样的道理)

所以,整个代码中跟模板的区别是,多了一步 确定查询节点跟根节点关系的代码 (代码中的函数名为work2)

 

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<cstdio>
  6 #define Rint register int
  7 #define mem(a,b) memset(a,(b),sizeof(a))
  8 #define Temp template<typename T>
  9 using namespace std;
 10 typedef long long ll;
 11 
 12 #define mid ((l+r)>>1)
 13 #define lson rt<<1,l,mid
 14 #define rson rt<<1|1,mid+1,r
 15 #define len (r-l+1)
 16 ll root,n;   //n需要作为全局变量
 17 const ll maxn=200000+10;
 18 //见题意
 19 ll e,beg[maxn],nex[maxn],to[maxn],w[maxn],wt[maxn];
 20 //链式前向星数组,w[]、wt[]初始点权数组
 21 ll a[maxn<<2],laz[maxn<<2];
 22 //线段树数组、lazy操作
 23 ll son[maxn],id[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn];
 24 //son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点
 25 ll res=0;
 26 //查询答案
 27 
 28 inline void add(ll x,ll y){//链式前向星加边
 29     to[++e]=y;
 30     nex[e]=beg[x];
 31     beg[x]=e;
 32 }
 33 //-------------------------------------- 以下为线段树
 34 inline void pushdown(ll rt,ll lenn){
 35     laz[rt<<1]+=laz[rt];
 36     laz[rt<<1|1]+=laz[rt];
 37     a[rt<<1]+=laz[rt]*(lenn-(lenn>>1));
 38     a[rt<<1|1]+=laz[rt]*(lenn>>1);
 39   //  a[rt<<1]%=mod;
 40   //  a[rt<<1|1]%=mod;
 41     laz[rt]=0;
 42 }
 43 
 44 inline void build(ll rt,ll l,ll r){
 45     if(l==r){
 46         a[rt]=wt[l];
 47    //     if(a[rt]>mod)a[rt]%=mod;
 48         return;
 49     }
 50     build(lson);
 51     build(rson);
 52     a[rt]=a[rt<<1]+a[rt<<1|1];
 53 }
 54 
 55 inline void query(ll rt,ll l,ll r,ll L,ll R){
 56     if(L<=l&&r<=R){res+=a[rt];return;}
 57     else{
 58         if(laz[rt])pushdown(rt,len);
 59         if(L<=mid)query(lson,L,R);
 60         if(R>mid)query(rson,L,R);
 61     }
 62 }
 63 
 64 inline void update(ll rt,ll l,ll r,ll L,ll R,ll k){
 65     if(L<=l&&r<=R){
 66         laz[rt]+=k;
 67         a[rt]+=k*len;
 68     }
 69     else{
 70         if(laz[rt])pushdown(rt,len);
 71         if(L<=mid)update(lson,L,R,k);
 72         if(R>mid)update(rson,L,R,k);
 73         a[rt]=a[rt<<1]+a[rt<<1|1];
 74     }
 75 }
 76 //---------------------------------以上为线段树
 77 inline ll qRange(ll x,ll y){
 78     ll ans=0;
 79     while(top[x]!=top[y]){//当两个点不在同一条链上
 80         if(dep[top[x]]<dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点
 81         res=0;
 82         query(1,1,n,id[top[x]],id[x]);//ans加上x点到x所在链顶端 这一段区间的点权和
 83         ans+=res;
 84        // ans%=mod;//按题意取模
 85         x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点
 86     }
 87     //直到两个点处于一条链上
 88     if(dep[x]>dep[y])swap(x,y);//把x点深度更深的那个点
 89     res=0;
 90     query(1,1,n,id[x],id[y]);//这时再加上此时两个点的区间和即可
 91     ans+=res;
 92     return ans;
 93 }
 94 
 95 inline void updRange(ll x,ll y,ll k){//同上
 96     //k%=mod;
 97     while(top[x]!=top[y]){
 98         if(dep[top[x]]<dep[top[y]])swap(x,y);
 99         update(1,1,n,id[top[x]],id[x],k);
100         x=fa[top[x]];
101     }
102     if(dep[x]>dep[y])swap(x,y);
103     update(1,1,n,id[x],id[y],k);
104 }
105 
106 inline ll qSon(ll x){
107     res=0;
108     query(1,1,n,id[x],id[x]+siz[x]-1);//子树区间右端点为id[x]+siz[x]-1
109     return res;
110 }
111 
112 inline void updSon(ll x,ll k){//同上
113     update(1,1,n,id[x],id[x]+siz[x]-1,k);
114 }
115 
116 inline void dfs1(ll x,ll f,ll deep){//x当前节点,f父亲,deep深度
117     dep[x]=deep;//标记每个点的深度
118     fa[x]=f;//标记每个点的父亲
119     siz[x]=1;//标记每个非叶子节点的子树大小
120     ll maxson=-1;//记录重儿子的儿子数
121     for(ll i=beg[x];i;i=nex[i]){
122         ll y=to[i];
123         if(y==f)continue;//若为父亲则continue
124         dfs1(y,x,deep+1);//dfs其儿子
125         siz[x]+=siz[y];//把它的儿子数加到它身上
126         if(siz[y]>maxson)son[x]=y,maxson=siz[y];//标记每个非叶子节点的重儿子编号
127     }
128 }
129 
130 inline void dfs2(ll x,ll topf){//x当前节点,topf当前链的最顶端的节点
131     id[x]=++cnt;//标记每个点的新编号
132     wt[cnt]=w[x];//把每个点的初始值赋到新编号上来
133     top[x]=topf;//这个点所在链的顶端
134     if(!son[x])return;//如果没有儿子则返回
135     dfs2(son[x],topf);//按先处理重儿子,再处理轻儿子的顺序递归处理
136     for(ll i=beg[x];i;i=nex[i]){
137         ll y=to[i];
138         if(y==fa[x]||y==son[x])continue;
139         dfs2(y,y);//对于每一个轻儿子都有一条从它自己开始的链
140     }
141 }
142 ll check2(ll xx,ll yy){
143     ll rt=xx,lst=yy;
144     while(top[xx]!=top[yy]){
145         if(dep[top[xx]]<dep[top[yy]]) swap(xx,yy);
146         lst=top[xx];xx=fa[top[xx]];
147     }
148     if(dep[xx]>dep[yy]) swap(xx,yy); 
149     if(xx!=rt) return 0;    //如果得出的点不是等于查询节点
150     //则说明没有关系,这里举个例子,比如在一个树中,他的父亲节点是根节点,
151     //那么就没有关系;换一个说法(如果yy不是xx的子树,则没有影响)
152     //两个节点没有关系,return 0
153     if(fa[lst]==xx) return lst; //如果lst的父亲是这个查询节点,则根节点 
154                                 //便是这个lst;
155     return son[xx];  //可以说return son[xx]是最难理解的了。
156 }   //不过还是理解了,开心! 如果fa[lst]的节点是查询节点的子节点或者孙节点;
157   //那么从他重儿子以下的点都是不在查询范围(更新范围)的,这里画图模拟一遍
158   //就会发现就是这么个道理。
159 int main()
160 {
161     scanf("%lld",&n);
162     for(ll i=1;i<=n;i++) scanf("%lld",&w[i]);
163     for(ll i=2;i<=n;i++){
164         ll t;
165         scanf("%lld",&t);
166         add(t,i);add(i,t);
167     }
168     ll m;
169     scanf("%lld",&m);
170     dfs1(1,0,1);
171     dfs2(1,1);
172     build(1,1,n);
173     root=1;
174     while(m--){
175         ll k,x,y,z;
176         scanf("%lld",&k);
177         if(k==1){
178             scanf("%lld",&root);
179         }
180         if(k==2){
181             scanf("%lld%lld%lld",&x,&y,&z);
182             updRange(x,y,z);
183         }
184         if(k==3){
185             scanf("%lld%lld",&x,&z);
186             if(x==root) {updSon(1,z);continue;}
187             y=check2(x,root);
188             if(y==0) updSon(x,z);
189             else{
190                 updSon(1,z);
191                 updSon(y,-z);
192             }
193         }
194         if(k==4){
195             scanf("%lld%lld",&x,&y);
196             printf("%lld\n",qRange(x,y));
197         }
198         if(k==5){
199             scanf("%lld",&x);
200             if(x==root){
201                 printf("%lld\n",qSon(1));
202                 continue;
203             }
204             y=check2(x,root);
205             if(y==0) printf("%lld\n",qSon(x));
206             else{
207                 printf("%lld\n",qSon(1)-qSon(y));
208             }
209         }
210     }
211     return 0;
212 }

 

posted @ 2019-11-03 20:35  古比  阅读(143)  评论(0编辑  收藏  举报