树链剖分 (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 ...
随机推荐
- 杭电oj_2058——The sum problem(java实现)
原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=2058 思路:等差数列公式变形:sum = a1 * len + len *(len -1)/2 抽象成 ...
- Spring域属性自动注入byName和byType
byName 方式 <!--byName约束:bean当中的域属性名必须跟所注入bean的id相同--> <bean id="student" class=&qu ...
- PHP 实现时间戳转化为几分钟前、几小时前等格式
//发布时间提示 function get_last_time($time) { // 当天最大时间 $todayLast = strtotime(date('Y-m-d 23:59:59')); $ ...
- 解决jquery.pjax加载后的异常滚动
个人博客 地址:http://www.wenhaofan.com/article/20181106154356 在使用jquery.pjax的时候发现每次加载完成后都会将滚动条滚动至顶部,用户体验极不 ...
- redis 有序集合(set),无需集合(zset)
1.set(无序集合)无序集合每个元素都是string元素的唯一性,不能重复没有修改操作 1.增加 sadd key value1 value2 value3... 2.获取 smembers key ...
- C/C++ Windows API——获取系统指定目录(转)
原文地址:C/C++ Windows API——获取系统指定目录 经测试,在win10 VS2017中用wprintf()输出正常,SHGetSpecialFolderPath函数也正常运行 但是用M ...
- jQuery---版本问题
jQuery的版本 官网下载地址:http://jquery.com/download/ jQuery版本有很多,分为1.x 2.x 3.x 大版本分类: 1.x版本:能够兼容IE678浏览器 2.x ...
- HDU1010 --- Tempter of the Bone(dfs+剪枝)
小明做了一个很久很久的梦,醒来后他竟发现自己和朋友在一个摇摇欲坠的大棋盘上,他们必须得想尽一切办法逃离这里.经过长时间的打探,小明发现,自己所在的棋盘格子上有个机关,上面写着“你只有一次机会,出发后t ...
- HDU 1542 Atlantis(扫描线算法)
题意:给出n个矩形的左下角左边和右上角坐标,求这n个矩形的面积并 原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=1542 典型的扫描线算法的题目 什么是 ...
- python 多版本环境
参考 https://www.cnblogs.com/---JoyceLiuHome/articles/7852871.html 安装 Anaconda集成化环境 https://www.anacon ...