洛谷 P3384树链剖分 题解
时间:2019-10-11
本文章向大家介绍洛谷 P3384树链剖分 题解,主要包括洛谷 P3384树链剖分 题解使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
挺好的一道树剖模板;
首先要学会最模板的树剖;
然后这道题要注意几个细节:
初始化时,seg[0]=1,seg[root]=1,top[root]=root,rev[1]=root;
在线段树上进行操作时,要使用lazy标记;
对于一个以x为根的子树,它子树中所有的元素一定时在线段树上连续的区间,且以seg[x]开始,以seg[x]+size[x]-1结束;
然后写码的时候注意不要手残(比如说预处理时写成了dep[u]=dep[u]+1);
#include <bits/stdc++.h> using namespace std; int n,m,r,p; int head[2000010],cnt; class littlestar{ public: int to; int nxt; void add(int u,int v){ to=v; nxt=head[u]; head[u]=cnt; } }star[2000010]; int a[100010]; int f[100010],dep[100010],son[100010],seg[100010],rev[100010],size[100010],top[100010]; void dfs1(int u,int fa) { size[u]=1; f[u]=fa; dep[u]=dep[fa]+1; for(int i=head[u];i;i=star[i].nxt){ int v=star[i].to; if(v==fa) continue; dfs1(v,u); size[u]+=size[v]; if(size[v]>size[son[u]]) son[u]=v; } } void dfs2(int u,int fa) { if(son[u]){ seg[son[u]]=++seg[0]; rev[seg[0]]=son[u]; top[son[u]]=top[u]; dfs2(son[u],u); } for(int i=head[u];i;i=star[i].nxt){ int v=star[i].to; if(v==fa) continue; if(!top[v]){ seg[v]=++seg[0]; rev[seg[0]]=v; top[v]=v; dfs2(v,u); } } } struct ss{ int sum; int lazy; }tree[1000010]; void build(int k,int l,int r) { if(l==r){ tree[k].sum=a[rev[l]]%p; return; } int mid=(l+r)/2; build(k<<1,l,mid); build(k<<1|1,mid+1,r); tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum; } void pre() { dfs1(r,0); seg[0]=seg[r]=1; top[r]=r; rev[1]=r; dfs2(r,0); build(1,1,seg[0]); } void pushdown(int k,int l,int r) { int mid=(l+r)/2; tree[k<<1].lazy=(tree[k<<1].lazy+tree[k].lazy)%p; tree[k<<1].sum=(tree[k<<1].sum+tree[k].lazy*(mid-l+1))%p; tree[k<<1|1].lazy=(tree[k<<1|1].lazy+tree[k].lazy)%p; tree[k<<1|1].sum=(tree[k<<1|1].sum+tree[k].lazy*(r-mid))%p; tree[k].lazy=0; } int query(int k,int l,int r,int x,int y) { if(r<x||l>y){ return 0; } if(l>=x&&r<=y){ return tree[k].sum%p; } int mid=(l+r)/2; pushdown(k,l,r); return (query(k<<1,l,mid,x,y)+query(k<<1|1,mid+1,r,x,y))%p; } void change(int k,int l,int r,int x,int y,int goal) { if(r<x||l>y) return; if(l>=x&&r<=y){ tree[k].sum=(tree[k].sum+(r-l+1)*goal)%p; tree[k].lazy=(tree[k].lazy+goal)%p; return; } pushdown(k,l,r); int mid=(l+r)/2; change(k<<1,l,mid,x,y,goal); change(k<<1|1,mid+1,r,x,y,goal); tree[k].sum=(tree[k<<1].sum+tree[k<<1|1].sum)%p; } void changeroad(int x,int y,int z) { int fx=top[x],fy=top[y]; while(fx!=fy){ if(dep[fx]<dep[fy]) swap(fx,fy),swap(x,y); change(1,1,seg[0],seg[fx],seg[x],z); x=f[fx]; fx=top[x]; } if(dep[x]>dep[y]) swap(x,y); change(1,1,seg[0],seg[x],seg[y],z); } int queryroad(int x,int y) { long long ans=0; int fx=top[x],fy=top[y]; while(fx!=fy){ if(dep[fx]<dep[fy]) swap(fx,fy),swap(x,y); ans=(ans+query(1,1,seg[0],seg[fx],seg[x]))%p; x=f[fx]; fx=top[x]; } if(dep[y]<dep[x]) swap(x,y); ans=(ans+query(1,1,seg[0],seg[x],seg[y]))%p; return ans%p; } void changetree(int x,int goal) { change(1,1,seg[0],seg[x],seg[x]+size[x]-1,goal); return; } long long querytree(int x) { long long res=0; res=(res+query(1,1,seg[0],seg[x],seg[x]+size[x]-1))%p; return res; } int main(){ cin>>n>>m>>r>>p; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } for(int i=1;i<=n-1;i++){ int u,v; scanf("%d%d",&u,&v); star[++cnt].add(u,v); star[++cnt].add(v,u); } pre(); for(int i=1;i<=m;i++){ int type; scanf("%d",&type); if(type==1){ int x,y,z; scanf("%d%d%d",&x,&y,&z); changeroad(x,y,z); } else if(type==2){ int x,y; scanf("%d%d",&x,&y); cout<<queryroad(x,y)%p<<endl; } else if(type==3){ int x,z; scanf("%d%d",&x,&z); changetree(x,z); } else{ int x; scanf("%d",&x); cout<<querytree(x)%p<<endl; } } } /* 5 5 2 30000 7 3 7 8 0 1 2 1 5 3 1 4 1 3 4 2 3 2 2 4 5 1 5 1 3 2 1 3 */
原文地址:https://www.cnblogs.com/kamimxr/p/11652696.html
- 【视频】自然框架之分页控件的使用方法(一) PostBack方式的一般分页方式
- Java中的Socket编程学习
- 【视频】自然框架之分页控件的使用方法(二) 下载、DLL说明和web.config的设置
- Jsp开发中遇到的中文乱码问题及解决方法
- 开发中最常见的Java字符串问题总结
- .net异步性能测试(包括ASP.NET MVC WebAPI异步方法)
- Java 8的函数式编程学习
- 在C++中反射调用.NET(一) 反射调用第一个.NET类的方法
- QuickPager分页控件,最简单的设置代码
- Java中有关Null的9问题
- 在C++中反射调用.NET(二) 定义数据接口 绑定委托方法 使用SOD DTO 对象 将.NET对象转换到C++结构体为何不使用序列化的问题
- Java阻塞队列线程集控制的实现方法
- 【自然框架】QuickPager分页控件,新增一种分页方式——伪URL分页(Postback版)
- 我们的漏洞Webug 3.0中级进阶攻略(上)
- 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 数组属性和方法
- vi与vim如何修改tab为4个空格方法示例
- Linux使用Crontab定时访问某个路由地址的方法
- jmeter 在linux服务器的安装和运行教程图解
- Linux中改变文件权限的chmod命令详析
- Ubuntu常用指令及用法详解
- Linux 系统使用私钥登录的教程详解
- Ubuntu如何轻松编译openJDK详解
- 详解Linux下Tomcat开启查看GC信息
- Centos7.4 zabbix3.4.7源码安装的方法步骤
- CentOS7环境搭建python3以及与python2实现共存的方法
- centos7.0安装离线JDK1.8方法详解
- ubuntu中swap(虚拟内存)设置方法
- 详解Linux监控重要进程的实现方法
- CentOS环境下安装Redis3.0及phpredis扩展测试示例
- 使用Apache commons-cli包进行命令行参数解析的示例代码