HDU 3966(树链剖分)
传送门
题意:
给你一个有nnn个顶点的树,树上的每一个点都有一个点权,现在有333种操作:
- IuvkI~ u~v~kI u v k,代表将结点uuu到结点vvv的最近的路径上的所有点的点权增加kkk
- DuvkD~ u~v~kD u v k ,代表将结点uuu到结点vvv的最近的路径上的所有点的点权减少kkk
- QuQ~ uQ u,代表求出结点uuu的点权。
题目分析:
树链剖分的模板题。
倘若只有操作111和操作222,则我们只需要用树上差分用O(n+m)\mathcal{O}(n+m)O(n+m)的时间复杂度完成操作。
但是,现在这个问题中,因此操作333的存在,使得用树上差分去做的话时间复杂度将会不优。
因此我们可以考虑采用树链剖分。
我们将树上的重链剖分出来之后,获取出他们的dfsdfsdfs序,并用数据结构(线段树/树状数组)去维护区间的和即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=50005;
struct Node{int to,next;
}q[100005];
struct ST{int sum,len,lazy;
}tr[maxn<<2];
int head[maxn],cnt=0,tot=0,f[maxn],dis[maxn],son[maxn],size[maxn],top[maxn];
int id[maxn],rk[maxn],v[maxn];
void add_edge(int from,int to){q[cnt].to=to;q[cnt].next=head[from];head[from]=cnt++;
}
void dfs1(int x){size[x]=1,dis[x]=dis[f[x]]+1;for(int i=head[x];i!=-1;i=q[i].next){int to=q[i].to;if(to==f[x]) continue;f[to]=x;dfs1(to);size[x]+=size[to];if(size[to]>size[son[x]]){son[x]=to;}}
}
void dfs2(int x,int t){top[x]=t;id[x]=++tot;rk[tot]=x;if(son[x]) dfs2(son[x],t);for(int i=head[x];i!=-1;i=q[i].next){int to=q[i].to;if(to==son[x]||to==f[x]) continue;dfs2(to,to);}
}
void push_up(int rt){tr[rt].sum=tr[rt<<1].sum+tr[rt<<1|1].sum;
}
void push_down(int rt){if(tr[rt].lazy){tr[rt<<1].sum+=1ll*tr[rt].lazy*tr[rt<<1].len;tr[rt<<1|1].sum+=1ll*tr[rt].lazy*tr[rt<<1|1].len;tr[rt<<1].lazy=tr[rt<<1].lazy+tr[rt].lazy;tr[rt<<1|1].lazy=tr[rt<<1|1].lazy+tr[rt].lazy;tr[rt].lazy=0;}
}
void build(int l,int r,int rt){tr[rt].lazy=0;tr[rt].len=r-l+1;if(l==r){tr[rt].sum=v[rk[l]];return ;}int mid=(l+r)>>1;build(l,mid,rt<<1);build(mid+1,r,rt<<1|1);push_up(rt);
}
void update(int L,int R,int l,int r,int rt,int k){if(L<=l&&R>=r){tr[rt].lazy=tr[rt].lazy+k;tr[rt].sum=tr[rt].sum+tr[rt].len*k*1l;return ;}push_down(rt);int mid=(l+r)>>1;if(L<=mid) update(L,R,l,mid,rt<<1,k);if(R>mid) update(L,R,mid+1,r,rt<<1|1,k);push_up(rt);
}
int query(int L,int R,int l,int r,int rt){if(L<=l&&R>=r){return tr[rt].sum;}push_down(rt);int mid=(l+r)>>1;int res=0;if(L<=mid) res+=query(L,R,l,mid,rt<<1);if(R>mid) res+=query(L,R,mid+1,r,rt<<1|1);return res;
}
int cal(int x,int y){int res=0;while(top[x]!=top[y]){if(dis[top[x]]<dis[top[y]]) swap(x,y);res+=query(id[top[x]],id[x],1,tot,1);x=f[top[x]];}if(id[x]>id[y]) swap(x,y);res+=query(id[x],id[y],1,tot,1);return res;
}
void UPDATE(int x,int y,int c){while(top[x]!=top[y]){if(dis[top[x]]<dis[top[y]]) swap(x,y);update(id[top[x]],id[x],1,tot,1,c);x=f[top[x]];}if(id[x]>id[y]) swap(x,y);update(id[x],id[y],1,tot,1,c);
}
int main()
{int n,m,t;while(~scanf("%d%d%d",&n,&m,&t)){memset(head,-1,sizeof(head));memset(dis,0,sizeof(dis));memset(son,0,sizeof(son));tot=0;cnt=0;for(int i=1;i<=n;i++) scanf("%d",&v[i]);for(int i=1;i<n;i++){int from,to;scanf("%d%d",&from,&to);add_edge(from,to);add_edge(to,from);}dfs1(1);dfs2(1,1);build(1,tot,1);while(t--){int x,y,z;char op[2];scanf("%s",op);if(op[0]=='I'){scanf("%d%d%d",&x,&y,&z);UPDATE(x,y,z);}if(op[0]=='D'){scanf("%d%d%d",&x,&y,&z);z=-z;UPDATE(x,y,z);}if(op[0]=='Q'){scanf("%d",&x);int res=query(id[x],id[x],1,tot,1);printf("%d\n",res);}}}return 0;
}
HDU 3966(树链剖分)相关推荐
- hdu 3966(树链剖分+线段树区间更新)
传送门:Problem 3966 https://www.cnblogs.com/violet-acmer/p/9711441.html 学习资料: [1]线段树区间更新:https://blog.c ...
- hdu 3966( 树链剖分+点权更新)
题意:给一棵树,并给定各个点权的值,然后有3种操作: I C1 C2 K: 把C1与C2的路径上的所有点权值加上K D C1 C2 K:把C1与C2的路径上的所有点权值减去K Q C:查询节点编号为C ...
- HDU 3966 树链剖分后线段树维护
题意: 一棵树, 操作1.$path(a,b)$之间的点权$+k$ 操作2.单点查询 题解: 树链剖分即可,注意代码细节,双向映射 主要是记录一下板子 #include <string.h> ...
- hdu 5274(树链剖分)
解题思路:这道题据说是树链剖分,所以也学习了一下. http://blog.sina.com.cn/s/blog_7a1746820100wp67.html 不同的是这里是点权值,我按照相似的处理方式 ...
- HDU 5405 (树链剖分+线段树)
Problem Sometimes Naive 题目大意 给你一棵n个节点的树,有点权. 要求支持两种操作: 操作1:更改某个节点的权值. 操作2:给定u,v, 求 Σw[i][j] i , j ...
- hdu 5052 树链剖分+线段树+区间合并
各种裸,但合在一起好恶心... 因为路径是有向的,所以要维护区间里向两个方向走的最大收益和区间里的最大最小值,要用到区间合并的线段树 #include <iostream> #includ ...
- HDU - 3966 Aragorn's Story(树链剖分)
题目传送门:HDU - 3966 Aragorn's Story 题目大意: 存在一个树,树上每个节点为一个阵营,阵营中存在敌人,现在要进行以下操作 I C1 C2 K :将阵营C1到阵营C2路 ...
- HDU 3966 POJ 3237 HYSBZ 2243 HRBUST 2064 树链剖分
树链剖分是一个很固定的套路 一般用来解决树上两点之间的路径更改与查询 思想是将一棵树分成不想交的几条链 并且由于dfs的顺序性 给每条链上的点或边标的号必定是连着的 那么每两个点之间的路径都可以拆成几 ...
- SPOJ- QTREE+HDU 3966(树链剖分裸题
题目:维护一个树,支持修改边长,查询任意两点间距离. 思路:学习了一下树链剖分,还是看了一阵子才看懂的(大概两个多小时orz...)其实总的来说并不是很难,主要是数组比较多,然后看的那篇博客大概是为了 ...
- HDU 3966 Aragorn's Story (树链剖分+线段树)
题意:给你一棵树,然后有三种操作 I L R K: 把L与R的路径上的所有点权值加上K D L R K:把L与R的路径上的所有点权值减去K Q X:查询节点编号为X的权值 思路:树链剖分裸题(我还没有 ...
最新文章
- Behave step matcher
- js之数组,对象,类数组对象
- configure: error: mcrypt.h not found. Please reins
- 操作系统和Web服务器那点事儿
- 奢侈品级别的广告位,到底要不要继续砸钱?
- 互联网人的恶梦是加班?不,是饥荒!
- java 读取csv_Java读取CSV的常用方法 | 学步园
- Python 小白从零开始 PyQt5 项目实战(7)折叠侧边栏的实现
- 工具使用-----Jmeter的基础用法
- 2020受欢迎的20个JavaScript 库
- linux下svn命令
- 给 IDEA 换个酷炫的主题吧,这个有点哇塞啊!
- 以太坊虚拟机 EVM(2)Solidity运行原理
- 官方授权正版老牌截图工具 Ashampoo Snap 12 中文网站 屏幕截图与视频录制工具
- hive 如何将数组转成字符串_HIve字符串函数
- FeedBurner 被阻尼,Feed 托管转到 FeedSky
- 一款让人耳目一新的事件驱动型RTOS
- 从零开始搭建SpringBoot项目(一)——开发环境搭建(图文详细)
- 习题9-2(免费糖果)【深搜dfs】+【记忆化搜索】
- SWFObject 2.0官方文档