BZOJ 树链剖分 1036: [ZJOI2008]树的统计Count
2014-04-09 17:06
344 查看
1036: [ZJOI2008]树的统计Count
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 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
41 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
41
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(); } }
相关文章推荐
- BZOJ 1036: [ZJOI2008]树的统计Count 树链剖分
- 树链剖分+线段树 BZOJ 1036 [ZJOI2008]树的统计Count
- 【块状树】【树链剖分】bzoj1036 [ZJOI2008]树的统计Count
- 【树链剖分模板】【bzoj1036】: [ZJOI2008]树的统计Count
- 【BZOJ1036】【ZJOI2008】【树的统计count】【树链剖分】
- 1036: [ZJOI2008]树的统计Count树链剖分
- [BZOJ]1036: [ZJOI2008]树的统计Count
- _bzoj1036 [ZJOI2008]树的统计Count【树链剖分】
- BZOJ1036: [ZJOI2008]树的统计Count
- 【BZOJ 1036】[ZJOI2008]树的统计Count 【树链剖分+线段树】
- BZOJ 1036: [ZJOI2008]树的统计Count (树链剖分模板题)
- BZOJ 1036 [ZJOI2008]树的统计Count==树剖裸题
- 树链剖分模板 1036: [ZJOI2008]树的统计Count
- BZoj 1036: [ZJOI2008]树的统计Count【树链剖分+线段树--模板题】
- BZOJ 1036: [ZJOI2008]树的统计Count(树链剖分)
- kyeremal-bzoj1036[ZJOI2008]-树的统计count-树链剖分
- bzoj 1036 [ZJOI2008]树的统计Count 树链剖分
- bzoj1036 [ZJOI2008]树的统计(树链剖分/lct)
- 树链剖分 - BZOJ 1036: [ZJOI2008]树的统计Count
- Bzoj 1036: [ZJOI2008]树的统计Count