洛谷P3833
Description
树链剖分板子题
考查两种操作
- A u v w 把 u 节点到 v 节点路径上所有节点权值加 w
- Q u 求以 u 为根节点的子树权值之和
首先需要了解线段树和 dfs 序,我这里没有很好的链接,不熟悉的再自行百度吧
另外了解树链剖分的思想(重儿子等等),否则会出很多千奇百怪的错误
树链剖分的构成
DFS1 来处理每个点的深度,他的父节点以及他的重儿子
DFS2 来处理每条链的链顶,每个点的 dfs 序和他们的 pre(建树时用)
然后就是各种操作函数
这里就不止说这个题了,顺便说一下树链剖分的其他几种操作
线段树也有很多操作,一些题可能会同时考到
但线段树就不说了,回去翻板子题的教程吧
分享几个树剖典型题目 P2590 P3178 和 P4315
Solution
操作一 区间加
树链剖分最常用操作之一
void change1(int x,int y,int val){
while(top[x]!=top[y]){
if(depth[top[x]]<depth[top[y]]) swap(x,y);
update(1,1,n,val,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(dfn[x]>dfn[y]) swap(x,y);
update(1,1,n,val,dfn[x],dfn[y]);
}
操作二 区间求和
int qsum1(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(depth[top[x]]<depth[top[y]]) swap(x,y);
ans+=query(1,1,n,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(dfn[x]>dfn[y]) swap(x,y);
ans+=query(1,1,n,dfn[x],dfn[y]);
return ans;
}
操作三 区间取最大值
int qmax(int x,int y){
int ans=-101010101;
while(top[x]!=top[y]){
if(depth[top[x]]<depth[top[y]]) swap(x,y);
ans=max(ans,qmax(1,1,n,dfn[top[x]],dfn[x]));
x=fa[top[x]];
}
if(dfn[x]>dfn[y]) swap(x,y);
ans=max(ans,qmax(1,1,n,dfn[x],dfn[y]));
return ans;
}
取最小值也是一样的
不过建树的时候注意处理最大值
操作四 子树上加
在以某点为根节点的子树上加值
void change2(int x,int val){update(1,dfn[x],dfn[x]+size[x]-1,val,1,n);}
看起来很简单对吧,其实只需要知道他的思想就好了
操作五 子树取和
int qsum2(int x){return query(1,dfn[x],dfn[x]+size[x]-1,1,n);}
这是我见的比较常用的几种操作
而且一些树链剖分的操作是不用专门来写函数的
就像上面的子树上操作一样
Code
再给下本题的代码,其实不是很必要了
没写注释,大家将就看吧
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#define maxn 10001000
#define INF 0x3f3f3f3f
#define int long long
#define lson x<<1
#define rson x<<1|1
using namespace std;
char s;
int n,q,cnt,tot,lazy[maxn],head[maxn],sum[maxn],a[maxn],size[maxn],dfn[maxn],depth[maxn],top[maxn],fa[maxn],pre[maxn],mmax[maxn],son[maxn];
struct edge{int fr,to,nxt;}e[maxn*2];
void addedge(int fr,int to){e[++tot].to=to;e[tot].nxt=head[fr];head[fr]=tot;}
int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0' &&ch<='9'){s=(s<<1)+(s<<3)+ch-'0';ch=getchar();}
return s*w;
}
namespace Seg{
void pushup(int x){sum[x]=sum[lson]+sum[rson];}
void pushdown(int x,int ln,int rn){
if(lazy[x]){
lazy[lson]+=lazy[x];
lazy[rson]+=lazy[x];
sum[lson]+=lazy[x]*ln;
sum[rson]+=lazy[x]*rn;
lazy[x]=0;
}
}
void build(int x,int l,int r){
lazy[x]=0;
if(l==r){sum[x]=0;return;}
int mid=(l+r)>>1;
build(lson,l,mid);build(rson,mid+1,r);
pushup(x);
}
void update(int x,int l,int r,int val,int L,int R){
if(L<=l &&r<=R){sum[x]+=(r-l+1)*val;lazy[x]+=val;return;}
int mid=(l+r)>>1;
pushdown(x,mid-l+1,r-mid);
if(L<=mid) update(lson,l,mid,val,L,R);
if(R>mid) update(rson,mid+1,r,val,L,R);
pushup(x);
}
int query(int x,int l,int r,int L,int R){
if(L<=l &&r<=R) return sum[x];
int ans=0;
int mid=(l+r)>>1;
pushdown(x,mid-l+1,r-mid);
if(l>R||r<L) return 0;
if(L<=mid) ans+=query(lson,l,mid,L,R);
if(R>mid) ans+=query(rson,mid+1,r,L,R);
return ans;
}
}
namespace Cut{
void dfs1(int x,int fat){
size[x]=1;depth[x]=depth[fat]+1;fa[x]=fat;
for(int i=head[x];i;i=e[i].nxt){
int to=e[i].to;
if(to==fat) continue;
dfs1(to,x);
size[x]+=size[to];
if(size[son[x]]<size[to]) son[x]=to;
}
}
void dfs2(int x,int tp){
top[x]=tp;dfn[x]=++cnt;pre[cnt]=x;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=e[i].nxt){
int to=e[i].to;
if(to==son[x]||to==fa[x]) continue;
dfs2(to,to);
}
}
void change1(int x,int y,int val){
while(top[x]!=top[y]){
if(depth[top[x]]<depth[top[y]]) swap(x,y);
Seg::update(1,1,n,val,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(dfn[x]>dfn[y]) swap(x,y);
Seg::update(1,1,n,val,dfn[x],dfn[y]);
}
int qsum1(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(depth[top[x]]<depth[top[y]]) swap(x,y);
ans+=Seg::query(1,1,n,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(dfn[x]>dfn[y]) swap(x,y);
ans+=Seg::query(1,1,n,dfn[x],dfn[y]);
return ans;
}
void change2(int x,int val){Seg::update(1,1,n,val,dfn[x],dfn[x]+size[x]-1);}
int qsum2(int x){return Seg::query(1,1,n,dfn[x],dfn[x]+size[x]-1);}
}
signed main(){
n=read();
// for(int i=1;i<=n;i++) a[i]=i-1;
for(int i=1,fs,es;i<n;i++){fs=read()+1;es=read()+1;addedge(fs,es);addedge(es,fs);}
Cut::dfs1(1,0);Cut::dfs2(1,1);Seg::build(1,1,n);
q=read();
for(int i=1,fs,es,ds;i<=q;i++){
cin>>s;
if(s =='A'){
fs=read()+1;es=read()+1;ds=read();
Cut::change1(fs,es,ds);
}
if(s =='Q'){
fs=read()+1;
printf("%lld\n",Cut::qsum2(fs));
}
}
return 0;
}
ps:
本题每个点的初始权值是 0 ,序号是 1 到 n,
我看成了初始权值为 1 到 n ,然后就 D 了好久
另外注意编号从 0 开始,要在输入加边或者 dfs 建树的地方处理一下
希望对大家有帮助
洛谷P3833的更多相关文章
- 树链剖分【洛谷P3833】 [SHOI2012]魔法树
P3833 [SHOI2012]魔法树 题目描述 Harry Potter 新学了一种魔法:可以让改变树上的果子个数.满心欢喜的他找到了一个巨大的果树,来试验他的新法术. 这棵果树共有N个节点,其中节 ...
- 洛谷——P3833 [SHOI2012]魔法树
P3833 [SHOI2012]魔法树 题目背景 SHOI2012 D2T3 题目描述 Harry Potter 新学了一种魔法:可以让改变树上的果子个数.满心欢喜的他找到了一个巨大的果树,来试验他的 ...
- [洛谷P3833][SHOI2012]魔法树
题目大意:给一棵树,路径加,子树求和 题解:树剖 卡点:无 C++ Code: #include <cstdio> #include <iostream> #define ma ...
- 洛谷P3833 [SHOI2012]魔法树(树链剖分)
传送门 树剖板子…… 一个路径加和,线段树上打标记.一个子树询问,dfs的时候记录一下子树的区间就行 // luogu-judger-enable-o2 //minamoto #include< ...
- 洛谷 P3833 [SHOI2012]魔法树
题目背景 SHOI2012 D2T3 题目描述 Harry Potter 新学了一种魔法:可以让改变树上的果子个数.满心欢喜的他找到了一个巨大的果树,来试验他的新法术. 这棵果树共有N个节点,其中节点 ...
- 洛谷1640 bzoj1854游戏 匈牙利就是又短又快
bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...
- 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.
没有上司的舞会 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...
- 洛谷P1108 低价购买[DP | LIS方案数]
题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...
- 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn Label:二维数组前缀和 你够了 这次我用DP
题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...
随机推荐
- 论文翻译:2018_Deep Learning for Acoustic Echo Cancellation in Noisy and Double-Talk Scenarios
论文地址:深度学习用于噪音和双语场景下的回声消除 博客地址:https://www.cnblogs.com/LXP-Never/p/14210359.html 摘要 传统的声学回声消除(AEC)通过使 ...
- Scanner详解
java.util.Scanner 是 Java5 的新特征,我们可以通过 Scanner 类来获取用户的输入 1)public static void main(String[] args) { S ...
- springboot 日期参数前后台转换问题
方式 一: 在实体类上加@DatetimeFormat与@JsonFormat注解 @DatetimeFormat:将前台日期字符串转换成Date格式 @DateTimeFormat(pattern= ...
- 单细胞分析实录(5): Seurat标准流程
前面我们已经学习了单细胞转录组分析的:使用Cell Ranger得到表达矩阵和doublet检测,今天我们开始Seurat标准流程的学习.这一部分的内容,网上有很多帖子,基本上都是把Seurat官网P ...
- Redis学习之路(三)常用命令总结
一.集群 查看Redis集群有多少个库 192.168.200.100:7001> config get databases 1) "databases" 2) " ...
- 编写 Dockerfile 生成自定义镜像
一般情况下我们可以从公共渠道诸如 DockerHub 获取镜像上获取镜像,但是在实际生产过程中,往往需要定制化的镜像,例如修改一些配置文件,增加一些特殊的命令或软件等需求,这时就需要通过编写 Dock ...
- [开源软件] 腾讯云Linux服务器一键安装LAMP/LNMP/LANMP环境 转
本帖最后由 我本戏子 于 2015-8-13 22:00 编辑OneinStack是非常优秀的一键PHP/JAVA安装脚本,提供以下环境:lnmp(Linux + Nginx+ MySQL+ PHP) ...
- Docker-Compose练习
运行一个镜像,需要添加大量的参数. 可以通过Docker-Compose编写这些参数. Docker-Compose可以帮助我们批量的管理容器. 只需要通过一个docker-compose.yml文件 ...
- Java反编译反混淆神器 - CFR
最近有大量jar包需要反编译后使用,但是由于jar包中的类被混淆过了,直接反编译以后的里面所有的变量都是一个名字.所以这里介绍一个反混淆神器:CRF. 不知道是不是官网的链接:http://www.b ...
- Redis Cluster 集群节点维护 (三)
Redis Cluster 集群节点维护: 集群运行很久之后,难免由于硬件故障,网络规划,业务增长,等原因对已有集群进行相应的调整,比如增加redis nodes 节点,减少节点,节点迁移,更换服务器 ...