0%

「ZJOI2008」树的统计

题目链接

题意

维护一棵树,支持单点修改权值、查询路径最大值和路径权值和。

思路

考虑重链剖分,使同一重链的 dfndfn 序连续。

对于查询操作,跳重链顶深度大的节点,同时区间查询这条链,维护区间最大值或总和。当两点在同一重链上时,再查询两点间对答案的贡献。

至于区间求 max\max 与和,可使用线段树维护。

实现

本题数据含有负数,在查询区间 max\max 时若查询区间与线段树维护的区间不能返回 00,而应返回 inf-inf

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include<cstdio>
using namespace std;
const int inf=0x3f3f3f3f;
int n,q,val[30001];
int tot,fir[30001],fa[30001],dep[30001],siz[30001],hson[30001],dfn[30001],rnk[30001],top[30001];
struct edge {
int to;
int nex;
}e[59999];
struct seg_tree {
int l,r,maxn,sum;
}t[120001];
inline int max(int x,int y)
{
return x>=y?x:y;
}
inline void add(int u,int v)
{
e[++tot]=edge{v,fir[u]};
fir[u]=tot;
return;
}
void dfs1(int u,int fa)
{
::fa[u]=fa,dep[u]=dep[fa]+1,siz[u]=1;
for(int i=fir[u],v;v=e[i].to,i;i=e[i].nex) {
if(v==fa) {
continue;
}
dfs1(v,u);
siz[u]+=siz[v],hson[u]=siz[v]>siz[hson[u]]?v:hson[u];
}
return;
}
void dfs2(int u,int top)
{
::top[u]=top,dfn[u]=++tot,rnk[tot]=u;
if(hson[u]) {
dfs2(hson[u],top);
}
for(int i=fir[u],v;v=e[i].to,i;i=e[i].nex) {
if((v==fa[u])||(v==hson[u])) {
continue;
}
dfs2(v,v);
}
return;
}
void build(int num,int l,int r)
{
t[num]=seg_tree{l,r,0,0};
if(l==r) {
t[num].maxn=t[num].sum=val[rnk[l]];
return;
}
int mid=(l+r)>>1;
build(num<<1,l,mid),build(num<<1|1,mid+1,r);
t[num].maxn=max(t[num<<1].maxn,t[num<<1|1].maxn),t[num].sum=t[num<<1].sum+t[num<<1|1].sum;
return;
}
void change(int num,int pos,int val)
{
if((t[num].l>pos)||(t[num].r<pos)) {
return;
}
if((t[num].l==pos)&&(t[num].r==pos)) {
t[num].maxn=t[num].sum=val;
return;
}
change(num<<1,pos,val),change(num<<1|1,pos,val);
t[num].maxn=max(t[num<<1].maxn,t[num<<1|1].maxn),t[num].sum=t[num<<1].sum+t[num<<1|1].sum;
return;
}
int get_max(int num,int l,int r)
{
if((t[num].l>r)||(t[num].r<l)) {
return -inf;
}
if((t[num].l>=l)&&(t[num].r<=r)) {
return t[num].maxn;
}
return max(get_max(num<<1,l,r),get_max(num<<1|1,l,r));
}
int get_sum(int num,int l,int r)
{
if((t[num].l>r)||(t[num].r<l)) {
return 0;
}
if((t[num].l>=l)&&(t[num].r<=r)) {
return t[num].sum;
}
return get_sum(num<<1,l,r)+get_sum(num<<1|1,l,r);
}
void Change()
{
int u,t;
scanf("%d%d",&u,&t);
change(1,dfn[u],t);
return;
}
void Max()
{
int u,v,res=-inf;
scanf("%d%d",&u,&v);
while(top[u]!=top[v]) {
if(dep[top[u]]>dep[top[v]]) {
res=max(res,get_max(1,dfn[top[u]],dfn[u]));
u=fa[top[u]];
} else {
res=max(res,get_max(1,dfn[top[v]],dfn[v]));
v=fa[top[v]];
}
}
res=max(res,dep[u]>dep[v]?get_max(1,dfn[v],dfn[u]):get_max(1,dfn[u],dfn[v]));
printf("%d\n",res);
return;
}
void Sum()
{
int u,v,res=0;
scanf("%d%d",&u,&v);
while(top[u]!=top[v]) {
if(dep[top[u]]>dep[top[v]]) {
res+=get_sum(1,dfn[top[u]],dfn[u]);
u=fa[top[u]];
} else {
res+=get_sum(1,dfn[top[v]],dfn[v]);
v=fa[top[v]];
}
}
res+=dep[u]>dep[v]?get_sum(1,dfn[v],dfn[u]):get_sum(1,dfn[u],dfn[v]);
printf("%d\n",res);
return;
}
int main()
{
scanf("%d",&n);
for(int i=1,u,v;i<n;++i) {
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs1(1,1);
tot=0,dfs2(1,1);
for(int i=1;i<=n;++i) {
scanf("%d",&val[i]);
}
build(1,1,n);
scanf("%d",&q);
while(q--) {
char s[10];
scanf("%s",s);
if(s[0]=='C') {
Change();
} else {
if(s[1]=='M') {
Max();
} else {
Sum();
}
}
}
return 0;
}