SPOJ6779(树剖+区间最大连续和)

本文介绍了一种解决树上区间查询与更新问题的方法——树链剖分结合线段树。通过树链剖分预处理,将树上的路径转换为链上区间,利用线段树实现高效查询与更新操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给定一棵树,有N(N≤100000)N(N≤100000)N(N100000)个节点,每一个节点都有一个权值xi(∣xi∣≤10000)x_i (|x_i| \le 10000)xi(xi10000)
你需要执行Q(Q≤100000))Q (Q \le 100000))Q(Q100000))次操作:

111 aaa bbb 查询(a,b)(a,b)(a,b)这条链上的最大子段和,可以为空(即输出000
222 aaa bbb ccc(a,b)(a,b)(a,b)这条链上的所有点权变为ccc (∣c∣≤10000)(|c| \le 10000)(c10000)

分析:

相较于SPOJ1716SPOJ1716SPOJ1716,变化就是从普通区间变到了树上,然后是区间修改(其实没差)。多用了一个树链剖分。
树链剖分部分和普通的也有一些区别,就是要分别记录左右两条链的信息,最后再合并才能得到最终答案,假设xxx是左链,yyy是右链就可。最后合并的时候还要注意一点:两个链的方向是相反的,也就是说这两个链的左右连续区间是方向是相反的,要交换其中一个的顺序再合并得出最终答案。

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;

const int maxn  = 4e5 + 5;
const int maxm  = 100 + 5;
const int inf   = 0x3f3f3f3f;
const LL  mod   = 1e9 + 7;//19260817
const double pi = acos(-1.0); 

int n, q, x, y, z, cnt, tot, opt, head[maxn], w[maxn];
int id[maxn], pre[maxn], son[maxn], size[maxn], deep[maxn], top[maxn], nw[maxn];

struct node{
	int to, next;
}edge[maxn << 1];

struct qwq{
	int w, lw, rw, maxw, lazy;
	qwq(){
		w = lw = rw = maxw = 0;
		lazy = inf;
	}
}a[maxn << 2];

qwq pushup(qwq x, qwq y){
	qwq res;
	res.w = x.w + y.w;
	res.lw = max(x.lw, x.w + y.lw);
	res.rw = max(y.rw, y.w + x.rw);
	res.maxw = max(max(x.maxw, y.maxw), x.rw + y.lw);
	return res;
}

void pushdown(int k, int l, int r, int mid){
	if(a[k].lazy != inf){
		a[k << 1].lazy = a[k << 1 | 1].lazy = a[k].lazy;
		a[k << 1].w = a[k].lazy * (mid - l + 1);
		a[k << 1].lw = a[k << 1].rw = a[k << 1].maxw = max(a[k << 1].w, 0);
		a[k << 1 | 1].w = a[k].lazy * (r - mid);
		a[k << 1 | 1].lw = a[k << 1 | 1].rw = a[k << 1 | 1].maxw = max(a[k << 1 | 1].w, 0);
		a[k].lazy = inf;
	}
}

void build(int k, int l, int r){
	if(l == r){
		a[k].lazy = inf;
		a[k].w = nw[l];
		a[k].lw = a[k].rw = a[k].maxw = max(nw[l], 0);
		return ;
	}
	int mid = l + r >> 1;
	build(k << 1, l, mid);
	build(k << 1 | 1, mid + 1, r);
	a[k] = pushup(a[k << 1], a[k << 1 | 1]);
}

qwq query(int k, int l, int r, int x, int y){
	if(l >= x && r <= y){
		return a[k];
	}
	int mid = l + r >> 1;
	pushdown(k, l, r, mid);
	qwq p, q;
	if(x <= mid) p = query(k << 1, l, mid, x, y);
	if(y > mid) q = query(k << 1 | 1, mid + 1, r, x, y);
	return pushup(p, q);
}

void update(int k, int l, int r, int x, int y, int z){
	if(l >= x && r <= y){
		a[k].lazy = z;
		a[k].w = (r - l + 1) * z;
		a[k].lw = a[k].rw = a[k].maxw = max(a[k].w, 0);
		return ;
	}
	int mid = l + r >> 1;
	pushdown(k, l, r, mid);
	if(x <= mid) update(k << 1, l, mid, x, y, z);
	if(y > mid) update(k << 1 | 1, mid + 1, r, x, y, z);
	a[k] = pushup(a[k << 1], a[k << 1 | 1]);
}

void addedge(int u, int v){
	edge[++cnt].to = v;
	edge[cnt].next = head[u];
	head[u] = cnt;
}

void dfs1(int x, int f, int d){
	pre[x] = f;
	deep[x] = d;
	size[x] = 1;
	int MAX = -1;
	for(int i = head[x]; i; i = edge[i].next){
		int v = edge[i].to;
		if(v == f) continue;
		dfs1(v, x, d + 1);
		size[x] += size[v];
		if(size[v] > MAX) MAX = size[v], son[x] = v;
	}
}

void dfs2(int x, int topf){
	id[x] = ++tot;
	nw[tot] = w[x];
	top[x] = topf;
	if(!son[x]) return ;
	dfs2(son[x], topf);
	for(int i = head[x]; i; i = edge[i].next){
		int v = edge[i].to;
		if(v == pre[x] || v == son[x]) continue;
		dfs2(v, v);
	}
}

qwq querySum(int x, int y){
	qwq L, R;
	while(top[x] != top[y]){
		if(deep[top[x]] < deep[top[y]]){
			R = pushup(query(1, 1, n, id[top[y]], id[y]), R);
			y = pre[top[y]];
		}else{
			L = pushup(query(1, 1, n, id[top[x]], id[x]), L);
			x = pre[top[x]];
		}
	}
	if(deep[x] > deep[y]){
		L = pushup(query(1, 1, n, id[y], id[x]), L);
	}else{
		R = pushup(query(1, 1, n, id[x], id[y]), R);
	}
	swap(L.lw, L.rw);
	return pushup(L, R);
} 

void updateSum(int x, int y, int z){
	while(top[x] != top[y]){
		if(deep[top[x]] < deep[top[y]]) swap(x, y);
		update(1, 1, n, id[top[x]], id[x], z);
		x = pre[top[x]];
	}
	if(deep[x] > deep[y]) swap(x, y);
	update(1, 1, n, id[x], id[y], z);
}

int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) scanf("%d", w + i);
	for(int i = 1; i <= n - 1; i++){
		scanf("%d %d", &x, &y);
		addedge(x, y), addedge(y, x);
	}
	dfs1(1, -1, 1);
	dfs2(1, 1);
	build(1, 1, n);
	scanf("%d", &q);
	while(q--){
		scanf("%d", &opt);
		if(opt == 1){
			scanf("%d %d", &x, &y);
			printf("%d\n", max(querySum(x, y).maxw, 0));
		}else if(opt == 2){
			scanf("%d %d %d", &x, &y, &z);
			updateSum(x, y, z);
		}
	}
    return 0;
}

代码巨丑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值