BZOJ 2157 「国家集训队」旅游(树链剖分,线段树,边权转点权)BZOJ计划

Posted 繁凡さん

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 2157 「国家集训队」旅游(树链剖分,线段树,边权转点权)BZOJ计划相关的知识,希望对你有一定的参考价值。

整理的算法模板合集: ACM模板

点我看算法全家桶系列!!!

实际上是一个全新的精炼模板整合计划


题目链接

https://hydro.ac/d/bzoj/p/2157

hydro 的 BZOJ 修复工程

Problem

给定一棵 n n n 个节点的树,边带权,编号 0 ∼ n − 1 0 \\sim n-1 0n1,需要支持五种操作:

C i w :将输入的第 i i i 条边权值改为 w w w
N u v :将 u , v u,v u,v 节点之间的边权都变为相反数
SUM u v :询问 u , v u,v u,v 节点之间边权和
MAX u v :询问 u , v u,v u,v 节点之间边权最大值
MIN u v :询问 u , v u,v u,v 节点之间边权最小值

保证任意时刻所有边的权值都在 [ − 1000 , 1000 ] [-1000,1000] [1000,1000] 内。

对于 100 % 100\\% 100% 的数据, 1 ≤ n , m ≤ 2 × 1 0 5 1\\le n,m \\le 2\\times 10^5 1n,m2×105

Solution

树上问题往往都是点带权,可以直接树链剖分解决。但是本题是边带权,考虑一个点最多只有一个父亲结点,我们可以将这个点与其父亲结点之间边的边权转化为这个点的点权即可。注意我们在进行树链剖分查询和修改路径的时候,端点 x x x 的父亲结点 f a [ x ] fa[x] fa[x] 是不在路径上的,因为父亲结点 f a [ x ] fa[x] fa[x] 的点权代表的是点 f a [ x ] fa[x] fa[x] 与它的父亲结点 f a [ f a [ x ] ] fa[fa[x]] fa[fa[x]] 之间的边权,因此,在查询和修改的时候,最后左端点为 i d [ x ] + 1 id[x]+1 id[x]+1,在实现的时候也要注意此类的细节。

转化为点权之后,树链剖分即可。

我们使用线段树维护题目中的五种操作,对于操作2,我们维护一个 tag 即可,每次修改的时候直接异或取反。

然后思维上就没有什么难度了,但是长是真的长,随随便便半个小时三百行代码呼你脸上…

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 6, maxm = maxn << 1 | 7, INF = 0x3f3f3f3f;

template <typename T> inline void read(T &x)
{
	x = 0;
	int f = 0;
	char ch = getchar();
	while(!isdigit(ch)) {f = ch == '-', ch = getchar();}
	while(isdigit(ch)) {x = x * 10 + ch - 48; ch = getchar();}
	if(f) x = -x;
}

template <typename T> void print(T x)
{
	if(x < 0) putchar ('-'), x = -x;
	if(x > 9) print (x / 10);
	putchar (x % 10 + '0');
}

int n, m, s, t, k;
ll ans;
int head[maxn], ver[maxm], edge[maxm], nex[maxm], tot;
bool vis[maxn];  
int val[maxn];
int a_after[maxn];

struct Edge
{
	int x, y;
}side[maxn];

void add(int x, int y, int z)
{
	ver[tot] = y;
	edge[tot] = z;
	nex[tot] = head[x];
	head[x] = tot ++ ;
}

void init()
{
	memset(head, -1, sizeof head);
	tot = 0;
}

struct Segment_Tree
{
	struct node 
	{
		int l, r;
		int sum;
//		int laz; 
		int tag;//相反数的tag
		int maxx;
		int minn;
	}tr[maxn << 2];
	
	void pushup(int p)
	{
		node &l = tr[p << 1], &r = tr[p << 1 | 1], &rt = tr[p];
		rt.sum = l.sum + r.sum;
		rt.maxx = max(l.maxx, r.maxx);
		rt.minn = min(l.minn, r.minn);
	}
	
	void pushdown(int p)
	{
		node &l = tr[p << 1], &r = tr[p << 1 | 1], &rt = tr[p];
		if(rt.tag == 0) return ;
		l.tag ^= 1, r.tag ^= 1;
		l.sum = -l.sum, r.sum = -r.sum;
		l.maxx = -l.maxx, r.maxx = -r.maxx;
		l.minn = -l.minn, r.minn = -r.minn;
		swap(l.maxx, l.minn), swap(r.maxx, r.minn);
		rt.tag = 0;
	}
	
	void build(int p, int l, int r)
	{
		tr[p].l = l, tr[p].r = r;
		if(l == r) {
			tr[p].sum = tr[p].minn = tr[p].maxx = a_after[l];
			return ;
		}	
		int mid = l + r >> 1;
		build(p << 1, l, mid);
		build(p << 1 | 1, mid + 1, r);
		pushup(p);
	}
	
	void modify(int p, int x, int k)
	{
		if(tr[p].l == tr[p].r) {
			tr[p].sum = tr[p].minn = tr[p].maxx = k;
			return ;
		}
		if(tr[p].tag) pushdown(p);
		int mid = tr[p].l + tr[p].r >> 1;
		if(x <= mid) modify(p << 1, x, k);
		if(x > mid) modify(p << 1 | 1, x, k);
		pushup(p);
	}
	
	void _reverse(int p, int l, int r)
	{
		if(tr[p].l >= l && tr[p].r <= r) {
			tr[p].tag ^= 1;
			tr[p].sum = -tr[p].sum;
			tr[p].minn = -tr[p].minn;
			tr[p].maxx = -tr[p].maxx;
			swap(tr[p].minn, tr[p].maxx);
			return ;
		}
		if(tr[p].tag) pushdown(p);
		int mid = tr[p].l + tr[p].r >> 1;
		if(l <= mid) _reverse(p << 1, l, r);
		if(r > mid) _reverse(p << 1 | 1, l, r);
		pushup(p);
	}
	
	int query_sum(int p, int l, int r)
	{
		int res = 0;
		if(tr[p].l >= l && tr[p].r <= r) return tr[p].sum;
		if(tr[p].tag) pushdown(p);
		int mid = tr[p].l + tr[p].r >> 1;
		if(l <= mid) res += query_sum(p << 1, l, r);
		if(r > mid) res += query_sum(p << 1 | 1, l, r);
		pushup(p);
		return res;
	}
	
	int query_max(int p, int l, int r)
	{
		int res = -INF;
		if(tr[p].l >= l && tr[p].r <= r) return tr[p].maxx;
		if(tr[p].tag) pushdown(p);
		int mid = tr[p].l + tr[p].r >> 1;
		if(l <= mid) res = max(res, query_max(p << 1, l, r));
		if(r > mid) res = max(res, query_max(p << 1 | 1, l, r));
		pushup(p);
		return res;
	}
	
	int query_min(int p, int l, int r)
	{
		int res = INF;
		if(tr[p].l >= l && tr[p].r <= r) return tr[p].minn;
		if(tr[p].tag) pushdown(p);
		int mid = tr[p].l + tr[p].r >> 1;
		if(l <= mid) res = min(res, query_min(p << 1, l, r));
		if(r > mid) res = min(res, query_min(p << 1 | 1, l, r));
		pushup(p);
		return res;
	}
	
}ST;

struct Tree
{ 
	int dfn[maxn], tim;
	int id[maxn];
	int siz[maxn], max_size;
	int hson[maxn];
	int w[maxn];
	int depth[maxn], fa[maxn], top[maxn];
	
	void dfs1(int x, int father, int deep)
	{
		depth[x] = deep + 1;
		fa[x] = father;
		siz[x] = 1;
		int max_sizes = -1;
		for (int i = head[x]; ~i; i = nex[i]) {
			int y = ver[i];
			ll z = edge[i];
			if(y == father) continue;
			dfs1(y, x, deep + 1);
			val[y] = z;
			siz[x] += siz[y];
			if(siz[y] > max_sizes) 
				max_sizes = siz[y], hson[x] = y;
		}	
	} 
	
	void dfs2(int x, int topfa)
	{
		dfn[x] = ++ tim;
		a_after[tim] = val[x];
		top[x] = topfa;
		if(hson[x] == 0) return ; 
		dfs2(hson[x], topfa); 
		for (int i = head[x]; ~i; i = nex[i]) {
			int y = ver[i]; 
			if(y == fa[x] || y == hson[x]) continue;
			dfs2(y, y);
		} 
	}
	
	void modify_change(int x, int y)
	{
		int pos;
		if(depth[side[x].x] > depth[side[x].y])
			pos = side[x].x;
		else pos = side[x].y;
		ST.modify(1, dfn[pos], y)

以上是关于BZOJ 2157 「国家集训队」旅游(树链剖分,线段树,边权转点权)BZOJ计划的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 2157 旅游(树链剖分+线段树)

BZOJ2157: 旅游 树链剖分 线段树

bzoj 2157: 旅游树链剖分+线段树

BZOJ 2157 旅行(树链剖分码农题)

bzoj 树链剖分题目汇总

LUOGU P1505 [国家集训队]旅游 (树链剖分+线段树)