kyeremal-bzoj1036[ZJOI2008]-树的统计count-树链剖分

bzoj1036[ZJOI2008]-树的统计count

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 7567   Solved: 3109
[ Submit][ Status][ Discuss]

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


树链剖分

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

#define rep(i, l, r) for (int i = l; i <= r; i++)
#define REP(i, l, r) for (int i = l; i >= r; i--)
#define INF 2147483647
#define MAXN 1000010

int n, N = -1, M = 0, m, first[MAXN], next[MAXN], NUM[MAXN], root, T_T, c[MAXN];
int dep[MAXN], siz[MAXN], son[MAXN], fa[MAXN], top[MAXN], w[MAXN];
struct tlist {int x, y;} a[MAXN];
bool vis[MAXN];
struct Tree {int l, r, lc, rc, sum, max;} tree[MAXN];

inline void swap(int &a, int &b) {int t = a; a = b; b = t;}
inline void add(int x, int y) {a[++N].x = x, a[N].y = y, next[N] = first[x], first[x] = N;}

inline void dfs(int x, int d) {
    dep[x] = d;
    siz[x] = 1;
    vis[x] = 1;
    int maxsize = 0, k = 0;
    for (int i = first[x]; ~i; i = next[i])
	if (!vis[a[i].y]) {
	    vis[a[i].y] = 1;
	    dfs(a[i].y, d+1);
	    siz[x] += siz[a[i].y];
	    if (siz[a[i].y] > maxsize) maxsize = siz[a[i].y], k = a[i].y;
	}
    son[x] = k;
}

inline void DFS(int x, int T) {
    top[x] = T;
    vis[x] = 1;
    if (son[x]) w[son[x]] = ++M, NUM[M] = son[x], DFS(son[x], T);
    for (int i = first[x]; ~i; i = next[i])
	if (!vis[a[i].y]) {
	    w[a[i].y] = ++M;
	    NUM[M] = a[i].y;
	    DFS(a[i].y, a[i].y);
	}
}

inline void build_tree(int i, int L, int R) {
    tree[i].l = L, tree[i].r = R;
    if (L == R) {tree[i].sum = tree[i].max = c[NUM[L]]; return;}
    build_tree(tree[i].lc = ++m, L, (L+R) >> 1);
    build_tree(tree[i].rc = ++m, ((L+R) >> 1) + 1, R);
    tree[i].max = max(tree[tree[i].lc].max, tree[tree[i].rc].max);
    tree[i].sum = tree[tree[i].lc].sum + tree[tree[i].rc].sum;
}

inline void modify(int i, int x, int cx) {
    int L = tree[i].l, R = tree[i].r;
    if (x < L || x > R) return;
    if (L == R) {tree[i].sum = tree[i].max = cx; return;}
    modify(tree[i].lc, x, cx);
    modify(tree[i].rc, x, cx);
    tree[i].max = max(tree[tree[i].lc].max, tree[tree[i].rc].max);
    tree[i].sum = tree[tree[i].lc].sum + tree[tree[i].rc].sum;
}

inline int query_max(int i, int ql, int qr) {
    int L = tree[i].l, R = tree[i].r;
    if (qr < L || ql > R) return -INF;
    if (L >= ql && R <= qr) return tree[i].max;
    return max(query_max(tree[i].lc, ql, qr), query_max(tree[i].rc, ql, qr));
}

inline int query_sum(int i, int ql, int qr) {
    int L = tree[i].l, R = tree[i].r;
    if (qr < L || ql > R) return 0;
    if (L >= ql && R <= qr) return tree[i].sum;
    return query_sum(tree[i].lc, ql, qr) + query_sum(tree[i].rc, ql, qr);
}

int main() {
    cin >> n;
    memset(first, -1, sizeof(first));
    memset(next, -1, sizeof(next));
    memset(fa, 0, sizeof(fa));
    rep(i, 1, n-1) {
	int tx, ty;
	scanf("%d%d", &tx, &ty);
	fa[ty] = tx;
	if (!fa[tx]) root = tx;
	add(tx, ty), add(ty, tx);
    }
    rep(i, 1, n) scanf("%d", &c[i]);
    memset(vis, 0, sizeof(vis));
    dfs(root, 1);
    memset(vis, 0, sizeof(vis));
    w[root] = ++M, NUM[M] = root;
    DFS(root, root);
    build_tree(m = 1, 1, n);
    cin >> T_T;
    while (T_T--) {
	char ch[MAXN];
	int tx, ty;
	scanf("%s", ch);
	scanf("%d%d", &tx, &ty);
	if (ch[0] == 'C') modify(1, w[tx], ty);
	if (ch[1] == 'M') {
	    int f1, f2, maxans = -INF;
	    while ((f1 = top[tx]) != (f2 = top[ty])) {
		if (dep[f1] < dep[f2]) swap(f1, f2), swap(tx, ty);
		maxans = max(maxans, query_max(1, w[f1], w[tx]));
		tx = fa[f1];
	    }
	    if (dep[tx] < dep[ty]) swap(tx, ty);
	    maxans = max(maxans, query_max(1, w[ty], w[tx]));
	    cout << maxans << endl;
	}
	if (ch[1] == 'S') {
	    int f1, f2, sumans = 0;
	    while ((f1 = top[tx]) != (f2 = top[ty])) {
		if (dep[f1] < dep[f2]) swap(f1, f2), swap(tx, ty);
		sumans += query_sum(1, w[f1], w[tx]);
		tx = fa[f1];
	    }
	    if (dep[tx] < dep[ty]) swap(tx, ty);
	    sumans += query_sum(1, w[ty], w[tx]);
	    cout << sumans << endl;
	}
    }
    return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值