BZOJ 树链剖分 1036: [ZJOI2008]树的统计Count

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 4231   Solved: 1764
[ Submit][ Status]

Description

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

Input

输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

Output

对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

Sample Input

4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

Sample Output

4
1
2
2
10
6
5
6
5
16

HINT

Source


题意:题意很易懂,给出一颗树,每个点有一个权值,有三个操作,一个是询问从u到v路径的上点的权值和,一个是询问u到v的路径上的点的权值的最大值,还有一个就是修改某个点的权值。


思路:这题很明显能用树链剖分来做的。我觉得树链剖分的基本思想就是把一段尽量长的路径放在线段树的连续的一段区域,不知道准不准确。这样就能把树分成了很多条链,那些长的叫做重链,短的叫做轻链。那么我们怎么求出一颗树的各条重链呢?我们首先求出每一颗子树的节点数,那么我们的重链延伸的方向就是子树中节点最多的子树,一直走到叶子,那么这一整条就是一个重链,没一条边放在线段树的连续的一段区域,并且为这条链经过的每一个点记录他们的链的开始位置。当然了我们还需要记录每个点(边)在线段树中的位置,这个是用于线段树更新和查询的。还需要记录每个点在树中的深度,这个在查询或者更新的时候需要用到。

假设我们是询问u->v,

那么不妨设dep[top[u]]>=dep[top[v]],如果top[u]==top[v],(dep是点的深度,top是点所在链的头结点,如果是轻链,那么top[u]=u) ,表明u和v在同一条重链上面,这时候我们能够返回结果了。如果top[u]!=top[v],那么我们就把u提到father[top[u][,顺带将这一段进行更新。 

更新和询问差不多的。

具体看看代码吧。


代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<cmath>
using namespace std;
const int maxn = 30000 + 5;
#pragma comment(linker,"/STACK:102400000,102400000")
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int w[maxn], tot, dep[maxn];
int node_w[maxn];
int pos[maxn];
int son[maxn], size[maxn];
int top[maxn], fa[maxn];
int N, Q;

inline int max(int a, int b) { return a > b ? a : b; }
inline int min(int a, int b) { return a < b ? a : b; }
struct SegmentTree
{
	int sum[maxn << 2], maxv[maxn << 2];
	void init()
	{
		memset(sum, 0, sizeof(sum));
	}
	void PushUp(int rt)
	{
		sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
		maxv[rt] = max(maxv[rt << 1], maxv[rt << 1 | 1]);
	}
	void Update(int p, int x,int l,int r,int rt)
	{
		if (l == r)
		{
			maxv[rt] = sum[rt] = x;
			return;
		}
		int m = (l + r) >> 1;
		if (p <= m) Update(p, x, lson);
		else Update(p, x, rson);
		PushUp(rt);
	}
	int query_max(int L, int R, int l, int r, int rt)
	{
		if (L <= l&&r <= R) return maxv[rt];
		int m = (l + r) >> 1;
		int ret = -1e9;
		if (L <= m) ret = max(ret, query_max(L, R, lson));
		if (m < R) ret = max(ret, query_max(L, R, rson));
		return ret;
	}
	int query_sum(int L, int R, int l, int r, int rt)
	{
		if (L <= l&&r <= R) return sum[rt];
		int m = (l + r) >> 1;
		int ret = 0;
		if (L <= m) ret += query_sum(L, R, lson);
		if (m < R) ret += query_sum(L, R, rson);
		return ret;
	}
}segtree;

struct Node
{
	int v;
	Node*next;
}*first[maxn],edges[maxn*2];
int m;

void add(int u, int v)
{
	edges[++m].v = v;
	edges[m].next = first[u];
	first[u] = &edges[m];
}

void input()
{
	m = 0;
	memset(first, 0, sizeof(first));
	for (int i = 0; i < N - 1; ++i)
	{
		int u, v;
		scanf("%d%d", &u, &v);
		add(u, v);
		add(v, u);
	}
	for (int i = 1; i <= N; ++i)
		scanf("%d", node_w + i);
}


void dfs1(int u, int f)
{
	fa[u] = f;
	for (Node*p = first[u]; p != NULL; p = p->next)
	{
		int v = p->v;
		if (v == f) continue;
		dep[v] = dep[u] + 1;
		dfs1(v, u);
		size[u] += size[v];
		if (son[u] == -1) son[u] = v;
		else if (size[son[u]] < size[v]) son[u] = v;
	}
	++size[u];
}

void dfs2(int u, int f)
{
	w[++tot] = u;  pos[u] = tot;
	if (son[u] != -1)
	{
		top[son[u]] = top[u];
		dfs2(son[u], u);
	}
	for (Node*p = first[u]; p != NULL; p = p->next)
	{
		int v = p->v;
		if (v == f || v==son[u]) continue;
		top[v] = v;
		dfs2(v,u);
	}
}

char oper[10];

void Update(int p, int x)
{
	segtree.Update(pos[p], x, 1, tot, 1);
}

int query_max(int u, int v)
{
	int ret = -1e9;
	while (true)
	{
		if (dep[top[u]] < dep[top[v]]) swap(u, v);
		if (top[u] == top[v])
		{
			int L = min(pos[u], pos[v]);
			int R = max(pos[u], pos[v]);
			ret = max(ret, segtree.query_max(L, R, 1, tot, 1));
			return ret;
		}
		else
		{
			int L = min(pos[u], pos[top[u]]);
			int R = max(pos[u], pos[top[u]]);
			ret = max(ret, segtree.query_max(L,R, 1, tot, 1));
			u = fa[top[u]];
		}
	}
}

int query_sum(int u, int v)
{
	int ret = 0;
	while (true)
	{
		if (dep[top[u]] < dep[top[v]]) swap(u, v);
		if (top[u] == top[v])
		{
			int L = min(pos[u], pos[v]);
			int R = max(pos[u], pos[v]);
			ret += segtree.query_sum(L, R, 1, tot, 1);
			return ret;
		}
		else
		{
			int L = min(pos[u], pos[top[u]]);
			int R = max(pos[u], pos[top[u]]);
			ret += segtree.query_sum(L, R, 1, tot, 1);
			u = fa[top[u]];
		}
	}
}

void solve()
{
	tot = 0;
	segtree.init();
	memset(size, 0, sizeof(size));
	memset(son, -1, sizeof(son));
	dep[1] = 1;
	dfs1(1,-1);
	top[1] = 1;
	dfs2(1,-1);
	for (int i = 1; i <= N; ++i)
		segtree.Update(pos[i], node_w[i], 1, tot, 1);
	scanf("%d", &Q);
	while (Q--)
	{
		int u, v;
		scanf("%s%d%d", &oper, &u, &v);
		if (oper[0] == 'C') Update(u, v);
		else if (oper[1] == 'M') printf("%d\n", query_max(u, v));
		else  printf("%d\n",query_sum(u, v));
	}
}

int main()
{
	//freopen("inpu.in", "r", stdin);
	while (scanf("%d", &N) == 1)
	{
		input();
		solve();
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值