树链剖分 (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 }