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

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

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

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

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

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

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

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

 #include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define Rint register int
#define mem(a,b) memset(a,(b),sizeof(a))
#define Temp template<typename T>
using namespace std;
typedef long long ll; #define mid ((l+r)>>1)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define len (r-l+1)
ll root,n; //n需要作为全局变量
const ll maxn=+;
//见题意
ll e,beg[maxn],nex[maxn],to[maxn],w[maxn],wt[maxn];
//链式前向星数组,w[]、wt[]初始点权数组
ll a[maxn<<],laz[maxn<<];
//线段树数组、lazy操作
ll son[maxn],id[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn];
//son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点
ll res=;
//查询答案 inline void add(ll x,ll y){//链式前向星加边
to[++e]=y;
nex[e]=beg[x];
beg[x]=e;
}
//-------------------------------------- 以下为线段树
inline void pushdown(ll rt,ll lenn){
laz[rt<<]+=laz[rt];
laz[rt<<|]+=laz[rt];
a[rt<<]+=laz[rt]*(lenn-(lenn>>));
a[rt<<|]+=laz[rt]*(lenn>>);
// a[rt<<1]%=mod;
// a[rt<<1|1]%=mod;
laz[rt]=;
} inline void build(ll rt,ll l,ll r){
if(l==r){
a[rt]=wt[l];
// if(a[rt]>mod)a[rt]%=mod;
return;
}
build(lson);
build(rson);
a[rt]=a[rt<<]+a[rt<<|];
} inline void query(ll rt,ll l,ll r,ll L,ll R){
if(L<=l&&r<=R){res+=a[rt];return;}
else{
if(laz[rt])pushdown(rt,len);
if(L<=mid)query(lson,L,R);
if(R>mid)query(rson,L,R);
}
} inline void update(ll rt,ll l,ll r,ll L,ll R,ll k){
if(L<=l&&r<=R){
laz[rt]+=k;
a[rt]+=k*len;
}
else{
if(laz[rt])pushdown(rt,len);
if(L<=mid)update(lson,L,R,k);
if(R>mid)update(rson,L,R,k);
a[rt]=a[rt<<]+a[rt<<|];
}
}
//---------------------------------以上为线段树
inline ll qRange(ll x,ll y){
ll ans=;
while(top[x]!=top[y]){//当两个点不在同一条链上
if(dep[top[x]]<dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点
res=;
query(,,n,id[top[x]],id[x]);//ans加上x点到x所在链顶端 这一段区间的点权和
ans+=res;
// ans%=mod;//按题意取模
x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点
}
//直到两个点处于一条链上
if(dep[x]>dep[y])swap(x,y);//把x点深度更深的那个点
res=;
query(,,n,id[x],id[y]);//这时再加上此时两个点的区间和即可
ans+=res;
return ans;
} inline void updRange(ll x,ll y,ll k){//同上
//k%=mod;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
update(,,n,id[top[x]],id[x],k);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
update(,,n,id[x],id[y],k);
} inline ll qSon(ll x){
res=;
query(,,n,id[x],id[x]+siz[x]-);//子树区间右端点为id[x]+siz[x]-1
return res;
} inline void updSon(ll x,ll k){//同上
update(,,n,id[x],id[x]+siz[x]-,k);
} inline void dfs1(ll x,ll f,ll deep){//x当前节点,f父亲,deep深度
dep[x]=deep;//标记每个点的深度
fa[x]=f;//标记每个点的父亲
siz[x]=;//标记每个非叶子节点的子树大小
ll maxson=-;//记录重儿子的儿子数
for(ll i=beg[x];i;i=nex[i]){
ll y=to[i];
if(y==f)continue;//若为父亲则continue
dfs1(y,x,deep+);//dfs其儿子
siz[x]+=siz[y];//把它的儿子数加到它身上
if(siz[y]>maxson)son[x]=y,maxson=siz[y];//标记每个非叶子节点的重儿子编号
}
} inline void dfs2(ll x,ll topf){//x当前节点,topf当前链的最顶端的节点
id[x]=++cnt;//标记每个点的新编号
wt[cnt]=w[x];//把每个点的初始值赋到新编号上来
top[x]=topf;//这个点所在链的顶端
if(!son[x])return;//如果没有儿子则返回
dfs2(son[x],topf);//按先处理重儿子,再处理轻儿子的顺序递归处理
for(ll i=beg[x];i;i=nex[i]){
ll y=to[i];
if(y==fa[x]||y==son[x])continue;
dfs2(y,y);//对于每一个轻儿子都有一条从它自己开始的链
}
}
ll check2(ll xx,ll yy){
ll rt=xx,lst=yy;
while(top[xx]!=top[yy]){
if(dep[top[xx]]<dep[top[yy]]) swap(xx,yy);
lst=top[xx];xx=fa[top[xx]];
}
if(dep[xx]>dep[yy]) swap(xx,yy);
if(xx!=rt) return ; //如果得出的点不是等于查询节点
//则说明没有关系,这里举个例子,比如在一个树中,他的父亲节点是根节点,
//那么就没有关系;换一个说法(如果yy不是xx的子树,则没有影响)
//两个节点没有关系,return 0
if(fa[lst]==xx) return lst; //如果lst的父亲是这个查询节点,则根节点
//便是这个lst;
return son[xx]; //可以说return son[xx]是最难理解的了。
} //不过还是理解了,开心! 如果fa[lst]的节点是查询节点的子节点或者孙节点;
//那么从他重儿子以下的点都是不在查询范围(更新范围)的,这里画图模拟一遍
//就会发现就是这么个道理。
int main()
{
scanf("%lld",&n);
for(ll i=;i<=n;i++) scanf("%lld",&w[i]);
for(ll i=;i<=n;i++){
ll t;
scanf("%lld",&t);
add(t,i);add(i,t);
}
ll m;
scanf("%lld",&m);
dfs1(,,);
dfs2(,);
build(,,n);
root=;
while(m--){
ll k,x,y,z;
scanf("%lld",&k);
if(k==){
scanf("%lld",&root);
}
if(k==){
scanf("%lld%lld%lld",&x,&y,&z);
updRange(x,y,z);
}
if(k==){
scanf("%lld%lld",&x,&z);
if(x==root) {updSon(,z);continue;}
y=check2(x,root);
if(y==) updSon(x,z);
else{
updSon(,z);
updSon(y,-z);
}
}
if(k==){
scanf("%lld%lld",&x,&y);
printf("%lld\n",qRange(x,y));
}
if(k==){
scanf("%lld",&x);
if(x==root){
printf("%lld\n",qSon());
continue;
}
y=check2(x,root);
if(y==) printf("%lld\n",qSon(x));
else{
printf("%lld\n",qSon()-qSon(y));
}
}
}
return ;
}

树链剖分 (ZQU1607)的更多相关文章

  1. BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

    3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2050  Solved: 817[Submit][Status ...

  2. BZOJ 1984: 月下“毛景树” [树链剖分 边权]

    1984: 月下“毛景树” Time Limit: 20 Sec  Memory Limit: 64 MBSubmit: 1728  Solved: 531[Submit][Status][Discu ...

  3. codevs 1228 苹果树 树链剖分讲解

    题目:codevs 1228 苹果树 链接:http://codevs.cn/problem/1228/ 看了这么多树链剖分的解释,几个小时后总算把树链剖分弄懂了. 树链剖分的功能:快速修改,查询树上 ...

  4. 并查集+树链剖分+线段树 HDOJ 5458 Stability(稳定性)

    题目链接 题意: 有n个点m条边的无向图,有环还有重边,a到b的稳定性的定义是有多少条边,单独删去会使a和b不连通.有两种操作: 1. 删去a到b的一条边 2. 询问a到b的稳定性 思路: 首先删边考 ...

  5. 树链剖分+线段树 CF 593D Happy Tree Party(快乐树聚会)

    题目链接 题意: 有n个点的一棵树,两种操作: 1. a到b的路径上,给一个y,对于路径上每一条边,进行操作,问最后的y: 2. 修改某个条边p的值为c 思路: 链上操作的问题,想树链剖分和LCT,对 ...

  6. 树链剖分+线段树 HDOJ 4897 Little Devil I(小恶魔)

    题目链接 题意: 给定一棵树,每条边有黑白两种颜色,初始都是白色,现在有三种操作: 1 u v:u到v路径(最短)上的边都取成相反的颜色 2 u v:u到v路径上相邻的边都取成相反的颜色(相邻即仅有一 ...

  7. bzoj2243树链剖分+染色段数

    终于做了一道不是一眼出思路的代码题(⊙o⊙) 之前没有接触过这种关于染色段数的题目(其实上课好像讲过),于是百度了一下(现在思维能力好弱) 实际上每一段有用的信息就是总共有几段和两段各是什么颜色,在开 ...

  8. bzoj3631树链剖分

    虽然是水题1A的感觉太爽了O(∩_∩)O~ 题意相当于n-1次树上路径上每个点权值+1,最后问每个点的权值 本来想写线段树,写好了change打算框架打完了再来补,结果打完发现只是区间加和单点查 前缀 ...

  9. BZOJ 3531: [Sdoi2014]旅行 [树链剖分]

    3531: [Sdoi2014]旅行 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1685  Solved: 751[Submit][Status] ...

  10. BZOJ 2243: [SDOI2011]染色 [树链剖分]

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6651  Solved: 2432[Submit][Status ...

随机推荐

  1. EL表达式无法获取boolean类型变量值

    今天调试个程序, 有个名为isAdmin的boolean类型的变量在jsp页面获取到的值为空, 这根本就是没获取到或者变量不存在的状况啊,但是在Action中明明是赋值成false了. 上网查了一下有 ...

  2. 转载:openmax基本概念

    https://yellowmax.blog.csdn.net/article/details/78080168 https://yellowmax.blog.csdn.net/article/det ...

  3. centos7添加多个网段

    # service network restart是重启所有网卡.例如下面的例子:>ifconfig eth0 up|down>service network restart|start| ...

  4. linux上部署springboot应用的脚本

    #!/bin/bash #getProcessId then kill pids=$(ps -ef | grep flashsale| awk '{print $2}') for pid in $pi ...

  5. mybatis(二):缘由

    本是Apache的一个开源项目iBatis 2010年,iBatis由Apache Software Foundation(软件基金会)迁移到了Google Code(代码托管平台),并改名为MyBa ...

  6. ajax中的参数

    function login() { $.ajax({ //几个参数需要注意一下 type: "POST",//方法类型 dataType: "json",// ...

  7. Pandas初体验之数据结构——Series和DataFrame

    Pandas是为了解决数据分析任务而创建的,纳入了大量的库和标准数据模型,提供了高效地操作大型数据集所需的工具. 对于Pandas包,在Python中常见的导入方法如下: from pandas im ...

  8. python使用libnum,gmpy2快速解RSA

    直接贴出Pcat师傅的解题脚本 # -*- coding:utf8 -*- __author__='pcat@chamd5.org' import libnum import gmpy2 n=7306 ...

  9. RFC3984: RTP Payload Format for H.264 Video(中文版)

    转载地址:https://blog.csdn.net/h514434485/article/details/51010950 官方文档,中文版本地址:http://www.rosoo.net/File ...

  10. jmeter 登陆--查询存在否-->新建客户-->查询存在否 + 压测

    1.登陆 正则表达式提取器和json提取器,都是后置处理器提取token(都可以在响应中以regexp tester 和 json path tester查看提取的对不对) beanshell 后置处 ...