【数据结构】——洛谷P3384 树链剖分
时间:2019-09-18
本文章向大家介绍【数据结构】——洛谷P3384 树链剖分,主要包括【数据结构】——洛谷P3384 树链剖分使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
蓝书上的树剖用不习惯,今日发现一篇julao的树剖题解,格式工整,简单易记,富有条理,因此学习之。
原题:GO
julao的blog:GO
具体的不讲了,直接上代码吧(已经过我的码风熏陶)
#include<bits/stdc++.h> using namespace std; const int N=2e5+10; int n,m,r,mod,tot,ans; int w[N],dep[N],siz[N],fa[N],son[N],top[N],id[N],w2[N]; int at[N<<1],sum[N<<1]; inline int read(){ int f=1,x=0; char c=getchar(); while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); } while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); } return x*f; } int head[N],cnt; struct edge{ int to,next; }e[N<<1]; inline void addedge(int from,int to){ e[++cnt]=(edge){to,head[from]};head[from]=cnt; } inline void add(int x,int y){addedge(x,y),addedge(y,x);} void dfs1(int u,int f){ dep[u]=dep[f]+1; siz[u]=1; fa[u]=f; int maxson=0; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v==f) continue; dfs1(v,u); siz[u]+=siz[v]; if(siz[v]>maxson) maxson=siz[v],son[u]=v; } } void dfs2(int u,int f){ id[u]=++tot; w2[tot]=w[u]; top[u]=f; if(!son[u]) return; dfs2(son[u],f); for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v==fa[u]||v==son[u]) continue; dfs2(v,v); } } inline int ls(int o){return o<<1;} inline int rs(int o){return o<<1|1;} inline void pd(int o,int l,int r,int num){ at[o]+=num; sum[o]+=(r-l+1)*num; sum[o]%=mod; } inline void pushdown(int o,int l,int r){ int mid=(l+r)>>1; pd(ls(o),l,mid,at[o]); pd(rs(o),mid+1,r,at[o]); at[o]=0; } inline void pushup(int o){ sum[o]=(sum[ls(o)]+sum[rs(o)])%mod; } void build(int o,int l,int r){ if(l==r){ sum[o]=w2[l]; if(sum[o]>mod) sum[o]%=mod; return; } int mid=(l+r)>>1; build(ls(o),l,mid); build(rs(o),mid+1,r); pushup(o); } void change(int o,int l,int r,int x,int y,int k){ if(l>y||r<x) return; if(x<=l&&r<=y){ sum[o]+=(r-l+1)*k; at[o]+=k; return; } int mid=(l+r)>>1; pushdown(o,l,r); if(x<=mid) change(ls(o),l,mid,x,y,k); if(y>mid) change(rs(o),mid+1,r,x,y,k); pushup(o); } void query(int o,int l,int r,int x,int y){ if(l>y||r<x) return; if(x<=l&&r<=y){ ans+=sum[o]; ans%=mod; return; } int mid=(l+r)>>1; pushdown(o,l,r); if(x<=mid) query(ls(o),l,mid,x,y); if(y>mid) query(rs(o),mid+1,r,x,y); } inline int ask(int u,int v){ int cur=0; while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]) swap(u,v); ans=0; query(1,1,n,id[top[u]],id[u]); cur+=ans; cur%=mod; u=fa[top[u]]; } ans=0; if(dep[u]>dep[v]) swap(u,v); query(1,1,n,id[u],id[v]); cur+=ans; return cur%mod; } inline void ask2(int u,int v,int k){ k%=mod; while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]) swap(u,v); change(1,1,n,id[top[u]],id[u],k); u=fa[top[u]]; } if(dep[u]>dep[v]) swap(u,v); change(1,1,n,id[u],id[v],k); } int main(){ n=read();m=read();r=read();mod=read(); for(register int i=1;i<=n;i++) w[i]=read(); int k,x,y,z; for(register int i=1;i<n;i++) add(read(),read()); dfs1(r,0); dfs2(r,r); build(1,1,n); for(register int i=1;i<=m;i++){ k=read(); if(k==1){ x=read();y=read();z=read(); ask2(x,y,z); } else if(k==2){ x=read();y=read(); printf("%d\n",ask(x,y)); } else if(k==3){ x=read();z=read(); change(1,1,n,id[x],id[x]+siz[x]-1,z); } else{ x=read();ans=0; query(1,1,n,id[x],id[x]+siz[x]-1); printf("%d\n",ans); } } return 0; }
原文地址:https://www.cnblogs.com/Nelson992770019/p/11545416.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 让小黑窗口听你指挥
- Element表单嵌套数据验证
- 摸鱼的新发现,滚动条无限滚动
- 理解装饰器是怎么使用的
- 第十一节:Activiti6.0——定时器开始事件、消息开始事件和错误开始事件介绍
- linux centos 安装mailx邮件服务器并测试发送一封邮件
- 深入分析Vue-Router原理,彻底看穿前端路由
- linux LVM 一键分区脚本自动扩容
- 再谈构造函数、原型、原型链之间的关系
- Java ConcurrentHashMap 高并发安全实现原理解析
- 第十二节:Activiti6.0——四种边界事件:定时器、错误、信号、补偿
- parted 磁盘分区-挂载-删除-shell脚本进行磁盘分区
- Ubuntu18.04——安装MySQL
- 八种 Vue 组件间通讯方式合集
- Sharding-JDBC 实现分库分表