题目描述

给定一棵树,设计数据结构支持以下操作
1 u v d  表示将路径 (u,v) 加d
2 u v 表示询问路径 (u,v) 上点权绝对值的和

输入

第一行两个整数n和m,表示结点个数和操作数
接下来一行n个整数a_i,表示点i的权值

接下来n-1行,每行两个整数u,v表示存在一条(u,v)的边

接下来m行,每行一个操作,输入格式见题目描述

输出

对于每个询问输出答案

样例输入

4 4
-4 1 5 -2
1 2
2 3
3 4
2 1 3
1 1 4 3
2 1 3
2 3 4

样例输出

10
13
9

提示

对于100%的数据,n,m <= 10^5 且 0<= d,|a_i|<= 10^8

如果都是正数直接树链剖分+线段树就行了。

现在有了负数,那不是再维护一个区间正数个数就好了?显然是不够的。

因为区间修改时会把一些负数变为正数,会改变区间正数的个数,所以我们要维护区间三个值:

1、区间绝对值之和

2、区间非负数个数

3、区间最大的负数

当每次修改一个区间时如果这个区间的最大负数会变成非负数,那么说明这个区间的非负数个数会改变,因此要重构这个区间。

怎么重构呢?

对于这个区间的左右子区间,对于不需要重构的子区间下传标记,对于需要重构的子区间就递归重构下去。

因为每个数最多只会被重构一次,因此重构均摊O(nlogn)。总时间复杂度还是O(mlogn)级别。

#include<set>
#include<map>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int num[800010];
int mx[800010];
ll sum[800010];
int d[100010];
int f[100010];
int son[100010];
int size[100010];
int top[100010];
int to[200010];
int tot;
int head[100010];
int s[100010];
int q[100010];
int n,m;
int x,y,z;
int opt;
int cnt;
ll a[800010];
int next[200010];
int v[100010];
int merge(int x,int y)
{
if(x<0&&y<0)
{
return max(x,y);
}
if(x<0)
{
return x;
}
if(y<0)
{
return y;
}
return 0;
}
void add(int x,int y)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
}
void dfs(int x)
{
size[x]=1;
d[x]=d[f[x]]+1;
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x])
{
f[to[i]]=x;
dfs(to[i]);
size[x]+=size[to[i]];
if(size[to[i]]>size[son[x]])
{
son[x]=to[i];
}
}
}
}
void dfs2(int x,int tp)
{
s[x]=++cnt;
top[x]=tp;
q[cnt]=x;
if(son[x])
{
dfs2(son[x],tp);
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x]&&to[i]!=son[x])
{
dfs2(to[i],to[i]);
}
}
}
void pushup(int rt)
{
num[rt]=num[rt<<1]+num[rt<<1|1];
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
mx[rt]=merge(mx[rt<<1],mx[rt<<1|1]);
}
void pushdown(int rt,bool x,bool y,int l,int r)
{
if(a[rt])
{
int mid=(l+r)>>1;
if(x)
{
if(mx[rt<<1])
{
mx[rt<<1]+=a[rt];
}
sum[rt<<1]+=1ll*(2*num[rt<<1]-(mid-l+1))*a[rt];
a[rt<<1]+=a[rt];
}
if(y)
{
if(mx[rt<<1|1])
{
mx[rt<<1|1]+=a[rt];
}
sum[rt<<1|1]+=1ll*(2*num[rt<<1|1]-(r-mid))*a[rt];
a[rt<<1|1]+=a[rt];
}
a[rt]=0;
}
}
void build(int rt,int l,int r)
{
if(l==r)
{
if(v[q[l]]<0)
{
mx[rt]=v[q[l]];
}
else
{
num[rt]=1;
}
sum[rt]=abs(v[q[l]]);
return ;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
void rebuild(int rt,int l,int r,ll c)
{
if(l==r)
{
num[rt]=1;
sum[rt]=mx[rt]+c;
mx[rt]=0;
return ;
}
int mid=(l+r)>>1;
c+=a[rt];
a[rt]=c;
if(mx[rt<<1]&&mx[rt<<1]+c>=0&&mx[rt<<1|1]&&mx[rt<<1|1]+c>=0)
{
a[rt]=0;
rebuild(rt<<1,l,mid,c);
rebuild(rt<<1|1,mid+1,r,c);
}
else if(mx[rt<<1]&&mx[rt<<1]+c>=0)
{
pushdown(rt,0,1,l,r);
rebuild(rt<<1,l,mid,c);
}
else if(mx[rt<<1|1]&&mx[rt<<1|1]+c>=0)
{
pushdown(rt,1,0,l,r);
rebuild(rt<<1|1,mid+1,r,c);
}
pushup(rt);
}
void change(int rt,int l,int r,int L,int R,int c)
{
if(L<=l&&r<=R)
{
if(mx[rt]+c>=0&&mx[rt])
{
rebuild(rt,l,r,c);
}
else
{
if(mx[rt])
{
mx[rt]+=c;
}
a[rt]+=c;
sum[rt]+=1ll*(2*num[rt]-(r-l+1))*c;
}
return ;
}
int mid=(l+r)>>1;
pushdown(rt,1,1,l,r);
if(L<=mid)
{
change(rt<<1,l,mid,L,R,c);
}
if(R>mid)
{
change(rt<<1|1,mid+1,r,L,R,c);
}
pushup(rt);
}
ll query(int rt,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
{
return sum[rt];
}
pushdown(rt,1,1,l,r);
int mid=(l+r)>>1;
long long res=0;
if(L<=mid)
{
res+=query(rt<<1,l,mid,L,R);
}
if(R>mid)
{
res+=query(rt<<1|1,mid+1,r,L,R);
}
return res;
}
void updata(int x,int y,int z)
{
while(top[x]!=top[y])
{
if(d[top[x]]<d[top[y]])
{
swap(x,y);
}
change(1,1,n,s[top[x]],s[x],z);
x=f[top[x]];
}
if(d[x]>d[y])
{
swap(x,y);
}
change(1,1,n,s[x],s[y],z);
}
ll downdata(int x,int y)
{
ll res=0;
while(top[x]!=top[y])
{
if(d[top[x]]<d[top[y]])
{
swap(x,y);
}
res+=query(1,1,n,s[top[x]],s[x]);
x=f[top[x]];
}
if(d[x]>d[y])
{
swap(x,y);
}
res+=query(1,1,n,s[x],s[y]);
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&v[i]);
}
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1);
dfs2(1,1);
build(1,1,n);
while(m--)
{
scanf("%d",&opt);
scanf("%d%d",&x,&y);
if(opt==1)
{
scanf("%d",&z);
updata(x,y,z);
}
else
{
printf("%lld\n",downdata(x,y));
}
}
}

BZOJ4127Abs——树链剖分+线段树的更多相关文章

  1. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  2. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  3. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  4. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

  5. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

  6. HDU4897 (树链剖分+线段树)

    Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...

  7. Aizu 2450 Do use segment tree 树链剖分+线段树

    Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...

  8. 【POJ3237】Tree(树链剖分+线段树)

    Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...

  9. HDU 2460 Network(双连通+树链剖分+线段树)

    HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链 ...

随机推荐

  1. 浅谈UI自动化测试

    最近一直在学习python,正好部门技术结构调整,就开始了点工向UI自动化测试的转变,我要说瞌睡来了就掉枕头么? 不过还好,可以将python的学习成果在自动化测试中实践... 1.about自动化测 ...

  2. JAVA体系的线程的实现,线程的调度,状态的转换

    java体系中线程的实现 1.使用内核线程实现 内核线程就是直接由操作系统内核支持的线程,这种线程由内核来完成线程切换,内核通过操作调度器对线程进行调度,并负责将线程的任务映射到各个处理器上,每个内核 ...

  3. ping + traceroute + tracert + tcpdump等命令的原理

    1.ping:关键就在这里,IP层协议通过机器B的IP地址和自己的子网掩码,发现它跟自己属同一网络,就直接在本网络内查找这台机器的. MAC如果以前两机有过通信,在 A 机的 ARP 缓存表应该 有 ...

  4. 5-(基础入门篇)学会刷Wi-Fi模块固件(刷LUA版本固件)

    http://www.cnblogs.com/yangfengwu/p/9065559.html 基础教程源码链接请在淘宝介绍中下载,由于链接很容易失效,如果失效请联系卖家,谢谢 https://it ...

  5. c语言程序设计 第一例子

    #include <studio.h> int main(){ printf("this is  dog.\n"); return 0; } studio.h 表示st ...

  6. ( 转)Ubuntu下创建、重命名、删除文件及文件夹,强制清空回收站方法

    Ubuntu下创建.重命名.删除文件及文件夹,强制清空回收站方法 mkdir 目录名 ——创建一个目录 rmdir 空目录名 ——删除一个空目录 rm 文件名 文件名 ——删除一个文件或多个文件 rm ...

  7. 清除EasyUi combotree下拉树的值

    由于测试自带的$(“node”).combotree("clear');问题始终解决不了 最终方法: Hdata是JSON数据源, 在它动态加在成功之后(节点全部显示出来,并且可以选择)再清 ...

  8. 大话设计模式之模板模式 C#

    学无止境,精益求精 十年河东,十年河西,莫欺少年穷 今天一起探讨模板模式,如下: 一.概念 上一篇文章讲了大话设计模式:原型模式,原型模式主要是通过Clone()方法<深浅复制>,创建新的 ...

  9. Java 基础之一对象导论

    对象导论 1.1 抽象过程 所有编程语言都提供抽象机制.人们所能解决的问题的复杂性直接取决于抽象的类型和质量. 汇编语言是对底层机器的轻微抽象. 我们将问题空间中的元素及其再解空间中的表示称为对象.这 ...

  10. layui表格和弹出框的简单示例

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...