HDU 3966 /// 树链剖分+树状数组
题意:
http://acm.hdu.edu.cn/showproblem.php?pid=3966
给一棵树,并给定各个点权的值,然后有3种操作:
I x y z : 把x到y的路径上的所有点权值加上z
D x y z:把x到y的路径上的所有点权值减去z
Q z:查询节点编号为x的权值
这里主要放下用树状数组维护的模板
区间修改单点查询 好像用线段树更好?
em.... 两种都放好了~
好像说hduoj是windows系统容易爆栈 手动扩栈加这句
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <bits/stdc++.h>
using namespace std;
#define mem(i,j) memset(i,j,sizeof(i))
#define LL long long
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1 const int maxn = 5e4 + ;
const int maxnode = maxn<<; LL head[maxn], tot, pos;
LL fa[maxn], son[maxn], dep[maxn], num[maxn];
// i的父亲、i的重结点、i的深度、i的儿子个数
LL top[maxn], p[maxn], fp[maxn];
// i所在链的顶端、ID->dfsID、dfsID->ID
LL n,m,q;
LL val[maxn];
struct Edge { int to,ne; }e[maxnode];
void init() {
tot=; pos=;
mem(head,); mem(son,);
}
void add(int u,int v) {
e[tot].to = v;
e[tot].ne = head[u];
head[u] = tot++;
} struct IntervalTree {
LL _sum, _min, _max;
LL sumv[maxnode], minv[maxnode], maxv[maxnode], setv[maxnode], addv[maxnode];
void init() {
mem(sumv,); mem(setv,); mem(addv,);
} void maintain(int L, int R, int rt) {
int lc = rt<<, rc = rt<<|;
if(R > L) {
sumv[rt] = sumv[lc] + sumv[rc];
minv[rt] = min(minv[lc], minv[rc]);
maxv[rt] = max(maxv[lc], maxv[rc]);
}
if(setv[rt] >= ) {
minv[rt] = maxv[rt] = setv[rt];
sumv[rt] = setv[rt] * (R-L+);
}
if(addv[rt]) {
minv[rt] += addv[rt]; maxv[rt] += addv[rt];
sumv[rt] =sumv[rt]+addv[rt] * (R-L+);
}
} void pushdown(int rt) {
int lc = rt*, rc = rt*+;
if(setv[rt] >= ) {
setv[lc] = setv[rc] = setv[rt];
addv[lc] = addv[rc] = ; setv[rt] = -;
}
if(addv[rt]) {
addv[lc] += addv[rt]; addv[rc] += addv[rt];
addv[rt] = ;
}
} ///update(更新区间左右端点、更新值、更新选项 op=1为加减 op!=1为置值、当前区间左右端点、根)
void update(int L, int R, LL v, int op, int l, int r, int rt){
//int lc = rt<<1, rc = rt<<1|1;
if(L <= l && R >= r) {
if(op == ) addv[rt] += v;
else { setv[rt] = v; addv[rt] = ; }
} else {
pushdown(rt);
int m = l + (r-l)/;
if(L <= m) update(L, R, v, op, lson);
else maintain(lson);
if(R > m) update(L, R, v, op, rson);
else maintain(rson);
}
maintain(l, r, rt);
} ///query(问询的左右端点、累加lazy_tag的累加量、当前区间左右端点、根)
void query(int L, int R, LL add, int l, int r, int rt) {
if(setv[rt] >= ) {
LL v = setv[rt] + add + addv[rt];
_sum += v * (LL)(min(r,R)-max(l,L)+);
_min = min(_min, v);
_max = max(_max, v);
} else if(L <= l && R >= r) {
_sum += sumv[rt] + add * (LL)(r-l+);
_min = min(_min, minv[rt] + add);
_max = max(_max, maxv[rt] + add);
} else {
int m = l + (r-l)/;
if(L <= m) query(L, R, add+addv[rt], lson);
if(R > m) query(L, R, add+addv[rt], rson);
}
}
}T; /** -----树链剖分----- */ void dfs1(int u,int pre,int d) {
dep[u]=d; fa[u]=pre; num[u]=;
for(int i=head[u];i;i=e[i].ne) {
int v=e[i].to;
if(v!=fa[u]) {
dfs1(v,u,d+); num[u]+=num[v];
if(!son[u] || num[v]>num[son[u]]) son[u]=v;
}
}
}
void dfs2(int u,int sp) {
top[u]=sp; p[u]=++pos; fp[p[u]]=u;
if(!son[u]) return; dfs2(son[u],sp);
for(int i=head[u];i;i=e[i].ne) {
int v=e[i].to;
if(v!=son[u] && v!=fa[u]) dfs2(v,v);
}
}
// 查询树上x到y的总和
LL queryPath(int x,int y) {
LL ans=0LL;
int fx=top[x], fy=top[y];
// fx==fy 说明到了LCA
while(fx!=fy) { // x y不在同一条重链上
if(dep[fx]>=dep[fy]) {
T._sum=0LL; T.query(p[fx],p[x],,,pos,);
ans=(ans+T._sum)%mod;
x=fa[fx];
} else {
T._sum=0LL; T.query(p[fy],p[y],,,pos,);
ans=(ans+T._sum)%mod;
y=fa[fy];
} // 先加离LCA更远的 且只加到父亲节点的一段 一步步移
fx=top[x], fy=top[y];
} // 直到两点在同一条重链上跳出 此时节点必连续 // 将最后到达LCA的一段连续的区间加上
if(p[x]>p[y]) swap(x,y);
T._sum=0LL; T.query(p[x],p[y],,,n,); return (ans+T._sum)%mod;
}
// 将树上x到y都加上z (和queryPath()差不多)
void updatePath(LL x,LL y,LL z) {
int fx=top[x], fy=top[y];
while(fx!=fy) {
if(dep[fx]>=dep[fy]) {
T.update(p[fx],p[x],z,,,n,);
x=fa[fx];
} else {
T.update(p[fy],p[y],z,,,n,);
y=fa[fy];
}
fx=top[x], fy=top[y];
} if(p[x]>p[y]) swap(x,y);
T.update(p[x],p[y],z,,,n,);
} /** ---------------- */ int main()
{
while(~scanf("%lld%lld%lld",&n,&m,&q)) {
init();
for(int i=;i<=n;i++)
scanf("%lld",&val[i]);
for(int i=;i<m;i++) {
int a,b; scanf("%d%d",&a,&b);
add(a,b); add(b,a);
}
dfs1(,,); // 根节点 前驱节点 深度
dfs2(,); // 当前节点 起始重结点
T.init();
for(int i=;i<=n;i++)
T.update(p[i],p[i],val[fp[p[i]]],,,n,); while(q--) {
LL x,y,z; char op;
scanf(" %c",&op); //printf("op%d\n",op);
if(op=='Q') {
scanf("%lld",&x);
T._sum=; T.query(p[x],p[x],0LL,,n,);
printf("%lld\n",T._sum);
} else {
scanf("%lld%lld%lld",&x,&y,&z);
if(op=='D') z=-z;
updatePath(x,y,z);
}
}
} return ;
} 线段树
线段树
#include <bits/stdc++.h>
using namespace std;
#define mem(i,j) memset(i,j,sizeof(i))
#define LL long long
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1 const int maxn = 5e4 + ;
const int maxnode = maxn<<;
const int maxedge = maxn<<; LL head[maxn], tot, pos;
LL fa[maxn], son[maxn], dep[maxn], num[maxn];
// i的父亲、i的重结点、i的深度、i的儿子个数
LL top[maxn], p[maxn], fp[maxn];
// i所在链的顶端、ID->dfsID、dfsID->ID
LL n,m,q;
LL val[maxn];
struct Edge { int to,ne; }e[maxedge];
void init() {
tot=; mem(head,);
pos=; mem(son,);
}
void add(int u,int v) {
e[tot].to = v;
e[tot].ne = head[u];
head[u] = tot++;
} struct Tree {
int N;
LL sumT[maxn];
void init() {
N=;
while(N<=n) N<<=;
mem(sumT,);
}
int lowbit(int i) { return -i&i; }
void add(int i,LL x) {
while(i<=N) {
sumT[i]+=x;
i+=lowbit(i);
}
}
LL sum(int i) {
LL res=0LL;
while(i) {
res+=sumT[i];
i-=lowbit(i);
} return res;
}
}T; /** -----树链剖分----- */ void dfs1(int u,int pre,int d) {
dep[u]=d; fa[u]=pre; num[u]=;
for(int i=head[u];i;i=e[i].ne) {
int v=e[i].to;
if(v!=fa[u]) {
dfs1(v,u,d+); num[u]+=num[v];
if(!son[u] || num[v]>num[son[u]]) son[u]=v;
}
}
}
void dfs2(int u,int sp) {
top[u]=sp; p[u]=++pos; fp[p[u]]=u;
if(!son[u]) return; dfs2(son[u],sp);
for(int i=head[u];i;i=e[i].ne) {
int v=e[i].to;
if(v!=son[u] && v!=fa[u]) dfs2(v,v);
}
}
// 将树上x到y都加上z (和queryPath()差不多)
void updatePath(LL x,LL y,LL z) {
int fx=top[x], fy=top[y];
while(fx!=fy) {
if(dep[fx]>=dep[fy]) {
T.add(p[fx],z); T.add(p[x]+,-z);
x=fa[fx];
} else {
T.add(p[fy],z); T.add(p[y]+,-z);
y=fa[fy];
}
fx=top[x], fy=top[y];
} if(p[x]>p[y]) swap(x,y);
T.add(p[x],z); T.add(p[y]+,-z);
} /** ---------------- */ int main()
{
while(~scanf("%lld%lld%lld",&n,&m,&q)) {
init();
for(int i=;i<=n;i++)
scanf("%lld",&val[i]);
for(int i=;i<m;i++) {
int a,b; scanf("%d%d",&a,&b);
add(a,b); add(b,a);
}
dfs1(,,); // 根节点 前驱节点 深度
dfs2(,); // 当前节点 起始重结点
T.init();
for(int i=;i<=n;i++)
T.add(p[i],val[i]),
T.add(p[i]+,-val[i]); while(q--) {
LL x,y,z; char op;
scanf(" %c",&op);
if(op=='Q') {
scanf("%lld",&x);
printf("%lld\n",T.sum(p[x]));
} else {
scanf("%lld%lld%lld",&x,&y,&z);
if(op=='D') z=-z;
updatePath(x,y,z);
}
}
} return ;
}
树状数组
HDU 3966 /// 树链剖分+树状数组的更多相关文章
- hdu 3966 Aragorn's Story(树链剖分+树状数组/线段树)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3966 题意: 给出一棵树,并给定各个点权的值,然后有3种操作: I C1 C2 K: 把C1与C2的路 ...
- Aragorn's Story 树链剖分+线段树 && 树链剖分+树状数组
Aragorn's Story 来源:http://www.fjutacm.com/Problem.jsp?pid=2710来源:http://acm.hdu.edu.cn/showproblem.p ...
- 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释
P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...
- HDU 3966 Aragorn's Story 树链剖分+树状数组 或 树链剖分+线段树
HDU 3966 Aragorn's Story 先把树剖成链,然后用树状数组维护: 讲真,研究了好久,还是没明白 树状数组这样实现"区间更新+单点查询"的原理... 神奇... ...
- hdu 3966 Aragorn's Story(树链剖分+树状数组)
pid=3966" target="_blank" style="">题目链接:hdu 3966 Aragorn's Story 题目大意:给定 ...
- HDU 3966 Aragorn's Story (树链剖分+树状数组)
Aragorn's Story Time Limit: 10000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- HDU 5044 (树链剖分+树状数组+点/边改查)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5044 题目大意:修改链上点,修改链上的边.查询所有点,查询所有边. 解题思路: 2014上海网赛的变 ...
- HDU 5293 Train chain Problem - 树链剖分(树状数组) + 线段树+ 树型dp
传送门 题目大意: 一颗n个点的树,给出m条链,第i条链的权值是\(w_i\),可以选择若干条不相交的链,求最大权值和. 题目分析: 树型dp: dp[u][0]表示不经过u节点,其子树的最优值,dp ...
- bzoj1146整体二分+树链剖分+树状数组
其实也没啥好说的 用树状数组可以O(logn)的查询 套一层整体二分就可以做到O(nlngn) 最后用树链剖分让序列上树 #include<cstdio> #include<cstr ...
随机推荐
- php开发面试题---1、php常用面试题一(PHP有哪些特性)
php开发面试题---1.php常用面试题一(PHP有哪些特性) 一.总结 一句话总结: ①.混合语法:php独特混合了C,Java,Prel以及PHP自创的语法. ②.为动态网页而生:可以比CGI或 ...
- 网络错误修复工具:Network Fault Repair Tool Build20160414
::请勿轻易修改此文件,以避免不可预知的错误 gwsbhqt@163.com @echo off color 0A setlocal enabledelayedexpansion title Netw ...
- jenkins在pipline中运行后台命令
需求 在jenkin中启动java程序或者python程序的时候,希望程序在后台执行,并在jenkins构建完成之后继续执行.在工作中有两个地方我碰到了这种需求 在shell script输入框中pi ...
- python轻松实现代码编码格式转换
python轻松实现代码编码格式转换 最近刚换工作不久,没太多的时间去整理工作中的东西,大部分时间都在用来熟悉新公司的业务,熟悉他们的代码框架了,最主要的是还有很多新东西要学,我之前主要是做php后台 ...
- 动态方法调用秘密武器 —— invokedynamic 指令解读 - MethodHandle
原文:https://juejin.im/book/5c25811a6fb9a049ec6b23ee/section/5ccc66dd518825403b5975fb import java.lang ...
- 拾遗:Git 常用操作回顾
温故而知新,可以为师矣. Git 布局 工作区---->暂存区---->本地仓库---->远程仓库 Create Repository git init PATH git add P ...
- Ubuntu14.04搭建JSP与Servlet开发环境及其测试详解
一,搭建JDK开发环境 1,在Java官网下载Jdk软件包,我的系统是64位Ubuntu14.04,所以选择jdk-8u25-linux-x64.tar.gz. 2,解压Jdk软件包 tar xvzf ...
- Spark中的多线程并发处理
Spark中的多任务处理 Spark的一个非常常见的用例是并行运行许多作业. 构建作业DAG后,Spark将这些任务分配到多个Executor上并行处理.但这并不能帮助我们在同一个Spark应用程序中 ...
- C# 反射入门
反射 别的用处先不管,至少在WinForm登录后的权限控制上有大用,比如登录后的窗体左侧树,点击通过字符串创建出窗体实例 案例如下图 AssTest类很简单 namespace assemblyTes ...
- vue中ref的使用(this.$refs获取为undefined)
如果你获取到的总是空的,你注意一下: 1.你在哪里调用,和你调用的对象 试试在mounted()里面调用有效果没有 调用的对象是本来就存在的,还是需要数据渲染之后才会出现的,同理,在mounted() ...