bzoj 4127: Abs(树链剖分+线段树)

4127: Abs

Time Limit: 40 Sec   Memory Limit: 256 MB
Submit: 667   Solved: 225
[ Submit][ Status][ Discuss]

Description

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

Input

第一行两个整数n和m,表示结点个数和操作数
接下来一行n个整数a_i,表示点i的权值
接下来n-1行,每行两个整数u,v表示存在一条(u,v)的边
接下来m行,每行一个操作,输入格式见题目描述

Output

对于每个询问输出答案

Sample Input

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

Sample Output

10
13
9


写完之后BUG一段,TLE+WA+RE了快50发

线段树节点存三个值:最大的负数,负数的个数,当前绝对值大小

叶子结点多存一个实际值

区间修改查询就直接区间操作

如果当前修改的那个区间全是正数或者最大的负数+当前值后还是负数,就懒惰

否则暴力修改

因为每次加的值都是正数,所以每个数改变符号只会发生1次,暴力操作只会进行n次,复杂度额外的O(nlogn)

#pragma comment(linker, "/STACK:102400000,102400000") 
#include<stdio.h>
#include<stdlib.h>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
typedef struct
{
	int to;
	int next;
}Road;
Road G[200005];
typedef struct Tree
{
	LL sum, rit;
	int mat, bet;
	Tree operator + (const Tree &b) const
	{
		Tree now;
		now.sum = sum+b.sum;
		now.mat = mat+b.mat;
		if(bet>=0)
			now.bet = b.bet;
		else if(b.bet>=0)
			now.bet = bet;
		else
			now.bet = max(bet, b.bet);
		return now;
	}
}Tree;
Tree tre[844444];
int head[150005], val[150005], fa[150005], son[150005], siz[150005], dep[150005];
int n, xt, cnt, rak[150005], id[150005], top[150005];
LL temp[844444];
void Add(LL u, LL v)
{
	cnt++;
	G[cnt].next = head[u];
	head[u] = cnt;
	G[cnt].to = v;
}
void Sech1(int u, int p)
{
	int i, v;
	fa[u] = p;
	dep[u] = dep[p]+1;
	for(i=head[u];i!=0;i=G[i].next)
	{
		v = G[i].to;
		if(v==p)
			continue;
		Sech1(v, u);
		siz[u] += siz[v]+1;
		if(son[u]==0 || siz[v]>siz[son[u]])
			son[u] = v;
	}
}
void Sech2(int u, int p)
{
	int i, v;
	top[u] = p;
	rak[u] = ++xt, id[xt] = u;
	if(son[u]==0)
		return;
	Sech2(son[u], p);
	for(i=head[u];i!=0;i=G[i].next)
	{
		v = G[i].to;
		if(son[u]==v || fa[u]==v)
			continue;
		Sech2(v, v);
	}
}
void Lazy(int l, int r, int x);
LL Query(int l, int r, int x, int a, int b);
void Create(int l, int r, int x);
void Update(int l, int r, int x, int a, int b, int c);
void Update_Tre(int x, int y, int d)
{
	int p1, p2;
	p1 = top[x], p2 = top[y];
	while(p1!=p2)
	{
		if(dep[p1]<dep[p2])
			swap(x, y), swap(p1, p2);
		Update(1, n, 1, rak[p1], rak[x], d);
		x = fa[p1], p1 = top[x];
	}
	if(dep[x]>dep[y])
		swap(x, y);
	Update(1, n, 1, rak[x], rak[y], d);
}
LL Query_Tre(int x, int y)
{
	LL sum = 0;
	int p1, p2;
	p1 = top[x], p2 = top[y];
	while(p1!=p2)
	{
		if(dep[p1]<dep[p2])
			swap(x, y), swap(p1, p2);
		sum += Query(1, n, 1, rak[p1], rak[x]);
		x = fa[p1], p1 = top[x];
	}
	if(dep[x]>dep[y])
		swap(x, y);
	sum += Query(1, n, 1, rak[x], rak[y]);
	return sum;
}
 
int main(void)
{
	int m, i, x, y, opt, d;
	//freopen("in.txt", "r", stdin);
	scanf("%d%d", &n, &m);
	for(i=1;i<=n;i++)
		scanf("%d", &val[i]);
	for(i=1;i<=n-1;i++)
	{
		scanf("%d%d", &x, &y);
		Add(x, y);
		Add(y, x);
	}
	Sech1(1, -1);
	Sech2(1, 1);
	Create(1, n, 1);
	while(m--)
	{
		scanf("%d%d%d", &opt, &x, &y);
		if(opt==1)
		{
			scanf("%d", &d);
			Update_Tre(x, y, d);
		}
		else
			printf("%lld\n", Query_Tre(x, y));
	}
	return 0;
}
 
void Create(int l, int r, int x)
{
	int m;
	if(l==r)
	{
		tre[x].rit = val[id[r]];
		tre[x].sum = abs(tre[x].rit);
		tre[x].bet = tre[x].rit;
		if(tre[x].rit<0)
			tre[x].mat = 1;
		else
			tre[x].mat = 0;
		return;
	}
	m = (l+r)/2;
	Create(l, m, x*2);
	Create(m+1, r, x*2+1);
	tre[x] = tre[x*2]+tre[x*2+1];
}
void Update(int l, int r, int x, int a, int b, int c)
{
	int m;
	if(l==r)
	{
		tre[x].rit += c;
		tre[x].sum = abs(tre[x].rit);
		tre[x].bet = tre[x].rit;
		if(tre[x].rit<0)
			tre[x].mat = 1;
		else
			tre[x].mat = 0;
		return;
	}
	if(temp[x])
		Lazy(l, r, x);
	if(l>=a && r<=b && (tre[x].mat==0 || tre[x].bet+c<0))
	{
		tre[x].bet += c;
		tre[x].sum += (LL)(r-l+1-2*tre[x].mat)*c;
		temp[x] += c;
		return;
	}
	m = (l+r)/2;
	if(a<=m)
		Update(l, m, x*2, a, b, c);
	if(b>=m+1)
		Update(m+1, r, x*2+1, a, b, c);
	tre[x] = tre[x*2]+tre[x*2+1];
}
LL Query(int l, int r, int x, int a, int b)
{
	int m;
	LL sum = 0;
	if(l>=a && r<=b)
		return tre[x].sum;
	if(temp[x])
		Lazy(l, r, x);
	m = (l+r)/2;
	if(a<=m)
		sum += Query(l, m, x*2, a, b);
	if(b>=m+1)
		sum += Query(m+1, r, x*2+1, a, b);
	return sum;
}
void Lazy(int l, int r, int x)
{
	int m;
	m = (l+r)/2;
	tre[x*2].bet += temp[x];
	tre[x*2+1].bet += temp[x];
	tre[x*2].sum += (m-l+1-2*tre[x*2].mat)*temp[x];
	tre[x*2+1].sum += (r-m-2*tre[x*2+1].mat)*temp[x];
	if(l==m)
		tre[x*2].rit += (m-l+1)*temp[x];
	if(m+1==r)
		tre[x*2+1].rit += (r-m)*temp[x];
	if(l<m)
		temp[x*2] += temp[x];
	if(r>m+1)
		temp[x*2+1] += temp[x];
	temp[x] = 0;
}
/*
5 100
-2 -1 0 1 2
1 2 2 3 3 4 4 5
1 1 5 2
2 1 5
*/



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值