树链剖分 (ZQU1607)
这道题与模板之间,多了个确定哪个为根的操作;
这道题有技巧,并不需要真正去建立以某个节点为根的树
关于路径的操作,无论以哪个点为根,得出的答案无影响;
关于对子节点进行操作的,有几种情况,
当查询节点刚好是根节点的话,就直接从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)的更多相关文章
- BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2050 Solved: 817[Submit][Status ...
- BZOJ 1984: 月下“毛景树” [树链剖分 边权]
1984: 月下“毛景树” Time Limit: 20 Sec Memory Limit: 64 MBSubmit: 1728 Solved: 531[Submit][Status][Discu ...
- codevs 1228 苹果树 树链剖分讲解
题目:codevs 1228 苹果树 链接:http://codevs.cn/problem/1228/ 看了这么多树链剖分的解释,几个小时后总算把树链剖分弄懂了. 树链剖分的功能:快速修改,查询树上 ...
- 并查集+树链剖分+线段树 HDOJ 5458 Stability(稳定性)
题目链接 题意: 有n个点m条边的无向图,有环还有重边,a到b的稳定性的定义是有多少条边,单独删去会使a和b不连通.有两种操作: 1. 删去a到b的一条边 2. 询问a到b的稳定性 思路: 首先删边考 ...
- 树链剖分+线段树 CF 593D Happy Tree Party(快乐树聚会)
题目链接 题意: 有n个点的一棵树,两种操作: 1. a到b的路径上,给一个y,对于路径上每一条边,进行操作,问最后的y: 2. 修改某个条边p的值为c 思路: 链上操作的问题,想树链剖分和LCT,对 ...
- 树链剖分+线段树 HDOJ 4897 Little Devil I(小恶魔)
题目链接 题意: 给定一棵树,每条边有黑白两种颜色,初始都是白色,现在有三种操作: 1 u v:u到v路径(最短)上的边都取成相反的颜色 2 u v:u到v路径上相邻的边都取成相反的颜色(相邻即仅有一 ...
- bzoj2243树链剖分+染色段数
终于做了一道不是一眼出思路的代码题(⊙o⊙) 之前没有接触过这种关于染色段数的题目(其实上课好像讲过),于是百度了一下(现在思维能力好弱) 实际上每一段有用的信息就是总共有几段和两段各是什么颜色,在开 ...
- bzoj3631树链剖分
虽然是水题1A的感觉太爽了O(∩_∩)O~ 题意相当于n-1次树上路径上每个点权值+1,最后问每个点的权值 本来想写线段树,写好了change打算框架打完了再来补,结果打完发现只是区间加和单点查 前缀 ...
- BZOJ 3531: [Sdoi2014]旅行 [树链剖分]
3531: [Sdoi2014]旅行 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1685 Solved: 751[Submit][Status] ...
- BZOJ 2243: [SDOI2011]染色 [树链剖分]
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 6651 Solved: 2432[Submit][Status ...
随机推荐
- ASP.NET MVC5 的请求管道和运行生命周期
https://www.jianshu.com/p/848fda7f79e0 请求处理管道 请求管道是一些用于处理HTTP请求的模块组合,在ASP.NET中,请求管道有两个核心组件:IHttpModu ...
- 《深入理解java虚拟机》读书笔记二——第三章
第三章 垃圾收集器与内存分配策略 1.判断对象是否已死 引用计数法: 给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,每当引用失效时,计数器值就减1. 任何时刻计数器为0的对象就是不 ...
- TensorFlow入门(矩阵基础)
1.placeholder 占位符 可以通过run方法传入值 测试代码如下: # encoding:utf-8 import tensorflow as tf # placeholder 占位符 可以 ...
- python之爬虫(爬取.ts文件并将其合并为.MP4文件——以及一些异常的注意事项)
//20200115 最近在看“咱们裸熊——we bears”第一季和第三季都看完了,单单就第二季死活找不到,只有腾讯有资源,但是要vip……而且还是国语版……所以就瞄上了一个视频网站——可以在线观看 ...
- Pytest学习9-常用插件
pytest-django:为django应用程序编写测试. pytest-twisted:为twisted应用程序编写测试,启动反应堆并处理测试函数的延迟. pytest-cov:覆盖率报告,与分布 ...
- USACO06DEC 牛奶模式
题意:求最长的可重叠的 K重复子串 的长度 考虑二分长度s,转化为验证性问题. 对SA进行分组.保证组内Height最小为s.这样在组内RMQ就可以任意了,因为RMQ一定是大于S的. 只要组内元素个数 ...
- nginx配置长连接(ajax60秒请求超时)
个人博客 地址:http://www.wenhaofan.com/article/20180911150337 1.在使用ajax做轮训的时候前台发出的ajax请求总是会在60秒之后返回405超时响应 ...
- asp.net中正则表达式使用
一.限定符:限定符提供了一种简单方法,用于指定允许特定字符或字符集自身重复出现的次数.限定符始终引用限定符前(左边)的模式,通常是单个字符,除非使用括号创建模式组. (一)非显示限定符 1. *,描述 ...
- 巨杉Tech | 十分钟快速搭建 Wordpress 博客系统
介绍 很多互联网应用程序开发人员第一个接触到的网站项目就是博客系统.而全球使用最广的Wordpress常常被用户用来快速搭建个人博客网站.默认情况下,Wordpress一般在后台使用MySQL关系型数 ...
- Web渗透测试思路整理
信息收集: 域名/IP 子域名列表 whois: 注册地址,注册人,联系方式等 whois反查: 同ip有哪些站点(旁注),同一个注册人注册了哪些域名 社工注册人信息 指纹识别: 操作系统及版本 数据 ...