树的统计
Posted hzoi-poozhai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树的统计相关的知识,希望对你有一定的参考价值。
题目描述
原题来自:ZJOI 2008
一树上有 个节点,编号分别为 到 ,每个节点都有一个权值 。我们将以下面的形式来要求你对这棵树完成一些操作:
CHANGE u t
:把节点 权值改为 ;QMAX u v
:询问点 到点 路径上的节点的最大权值;QSUM u v
:询问点 到点 路径上的节点的权值和。
注意:从点 到点 路径上的节点包括 和 本身。
输入格式
第一行为一个数 ,表示节点个数;
接下来 行,每行两个整数 ,表示节点 与节点 之间有一条边相连;
接下来 行,每行一个整数,第 行的整数 表示节点 的权值;
接下来一行,为一个整数 ,表示操作总数;
接下来 行,每行一个操作,以 CHANGE u t
或 QMAX u v
或 QSUM u v
的形式给出。
输出格式
对于每个 QMAX
或 QSUM
的操作,每行输出一个整数表示要求的结果。
样例
样例输入
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
样例输出
4
1
2
2
10
6
5
6
5
16
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e5+1000;
int f[maxn], head[maxn], mod, rt, len, dfn[maxn], n, m, son[maxn], size[maxn], a[maxn], top[maxn], dep[maxn], cnt, rk[maxn];
struct edge{ int to, next; } e[2*maxn];
struct node{int l, r, w, siz, maxx; } tr[maxn<<2];
void add(int x, int y){ e[++len].to=y; e[len].next=head[x]; head[x]=len; }
void dfs1(int u, int fa){
size[u] = 1;
for(int i=head[u]; ~i; i=e[i].next){
int v = e[i].to;
if(v==fa) continue;
dep[v] = dep[u] + 1;
f[v] = u;
dfs1(v, u);
size[u] += size[v];
if(!son[u] || size[v]>size[son[u]]) son[u] = v;
}
}
void dfs2 (int u, int tp){
top[u] = tp;
dfn[u] = ++cnt;
rk[cnt] = a[u];
if(son[u]) dfs2(son[u], tp);
for(int i=head[u]; ~i; i=e[i].next){
int v = e[i].to;
if(v!=son[u] && v!=f[u]) dfs2(v, v);
}
}
void pushup(int u){ tr[u].w = tr[u<<1].w + tr[u<<1|1].w; }
void change(int u, int x, int w){
if(tr[u].l == tr[u].r){ tr[u].w = tr[u].maxx = w; return; }
int mid = (tr[u].l + tr[u].r)>>1;
if(x<=mid) change(u<<1, x, w);
else change(u<<1|1, x, w);
tr[u].maxx = max(tr[u<<1].maxx, tr[u<<1|1].maxx);
pushup(u);
}
void build(int u, int l, int r){
tr[u].l = l, tr[u].r = r, tr[u].siz = r - l + 1;
if(l==r){ tr[u].w = tr[u].maxx = rk[l]; return; }
int mid = (l+r)>>1;
build(u<<1, l, mid); build(u<<1|1, mid+1, r);
tr[u].maxx = max(tr[u<<1].maxx, tr[u<<1|1].maxx);
pushup(u);
}
int query(int u, int l, int r){
int ans = 0;
if(l<=tr[u].l && r>=tr[u].r) return tr[u].w;
int mid = (tr[u].l + tr[u].r) >> 1;
if(l<=mid) ans = ans + query(u<<1, l, r);
if(r>mid) ans = ans + query(u<<1|1, l, r);
return ans;
}
int querysum(int u, int v){
int ans = 0;
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
ans += query(1, dfn[top[u]], dfn[u]);
u = f[top[u]];
}
if(dep[u] > dep[v]) swap(u, v);
ans += query(1, dfn[u], dfn[v]);
return ans;
}
int getmax(int u, int l, int r){
if(l<=tr[u].l && r>=tr[u].r) return tr[u].maxx;
int ans = -99999999;
int mid = (tr[u].l + tr[u].r) >> 1;
if(l<=mid) ans = max(ans, getmax(u<<1, l, r));
if(r>mid) ans = max(ans, getmax(u<<1|1, l, r));
return ans;
}
int treemax(int x, int y){
int res = -999999;
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]]) swap(x, y);
res = max(res, getmax(1, dfn[top[x]], dfn[x]));
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
res = max(res, getmax(1, dfn[x], dfn[y]));
return res;
}
int main(){
memset(head, -1, sizeof(head));
scanf("%d", &n);
for(int i=1; i<n; i++){
int x, y; scanf("%d%d", &x, &y);
add(x, y); add(y, x);
}
for(int i=1; i<=n; i++) scanf("%d", &a[i]);
int q; scanf("%d", &q);
dfs1(1, 0);
dfs2(1, 1);
build(1, 1, n);
while(q--){
int u, v;
char ch[10]; scanf("%s%d%d", ch, &u, &v);
if(ch[1] == ‘M‘) printf("%d
", treemax(u, v));
else if(ch[1] == ‘S‘) printf("%d
", querysum(u, v));
else if(ch[1] == ‘H‘) change(1, dfn[u], v);
}
return 0;
}
以上是关于树的统计的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode810. 黑板异或游戏/455. 分发饼干/剑指Offer 53 - I. 在排序数组中查找数字 I/53 - II. 0~n-1中缺失的数字/54. 二叉搜索树的第k大节点(代码片段
bzoj1036 [ZJOI2008]树的统计Count——LCT