题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4034
4034: [HAOI2015]树上操作
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 7576 Solved: 2597
[Submit][Status][Discuss]
Description
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
Input
第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1
行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。再接下来 M 行,每行分别表示一次操作。其中
第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
Output
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
Sample Input
5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3
Sample Output
6
9
13
思路:好吧,其实这道题并不是难题,而是一道树链剖分的入门题。由于我太弱了,这道题仍然给我带来了一定的困扰,卡了我整整一个下午。最后发现有一个地方少判断了一种情况,QAQ......
首先修改点权很常规,板子一套就好了。
修改子树时,需找到当前点的dfs序的范围,也就是在刚dfs到这个点时,记录一下是第几个,等dfs完它的所有儿子时,mx[u]=max(mx[u],mx[son]),也就是将它的最大范围更新为其所有儿子能达到的最大范围。然后用线段树区间更新就好了。
查询时就是裸的线段树区间查询,自己再搞一搞就好了。。。
AC代码(又写了200多行)
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
using namespace std;
typedef long long LL;
typedef long long ll;
const int MAXN = 100010;
struct Edge
{
int to, next;
} edge[MAXN<<2];
int n;
int a[MAXN];
int head[MAXN], tot;
int top[MAXN];
int fa[MAXN];
int deep[MAXN];
int num[MAXN];
int p[MAXN];
int fp[MAXN];
int son[MAXN];
int pos;
int mx[MAXN];
void init()
{
tot = 0;
memset(head, -1, sizeof(head));
pos = 0;
memset(son, -1, sizeof(son));
return ;
}
void addedge(int u, int v)
{
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
return ;
}
void dfs1(int u, int pre, int d)
{
deep[u] = d;
fa[u] = pre;
num[u] = 1;
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if (v != pre)
{
dfs1(v, u, d + 1);
num[u] += num[v];
if (son[u] == -1 || num[v] > num[son[u]])
{
son[u] = v;
}
}
}
return ;
}
void dfs2(int u, int sp)
{
top[u] = sp;
++pos;
p[u] = mx[u] = pos;
fp[p[u]] = u;
if (son[u] == -1)
{
return ;
}
dfs2(son[u], sp);
mx[u]=max(mx[u],mx[son[u]]);
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if (v != son[u] && v != fa[u])
{
dfs2(v, v);
mx[u]=max(mx[u],mx[v]);
}
}
return ;
}
LL add[MAXN<<2];
LL sum[MAXN<<2];
void PushUp(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void PushDown(int rt,int m) {
if (add[rt]) {
add[rt<<1] += add[rt];
add[rt<<1|1] += add[rt];
sum[rt<<1] += add[rt] * (m - (m >> 1));
sum[rt<<1|1] += add[rt] * (m >> 1);
add[rt] = 0;
}
}
void build(int l,int r,int rt) {
add[rt] = 0;
if (l == r)
{
sum[rt]=a[fp[l]];
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUp(rt);
}
void update(int L,int R,ll c,int l,int r,int rt) {
if (L <= l && r <= R) {
add[rt] += c;
sum[rt] += (LL)c * (r - l + 1);
return ;
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
if (L <= m) update(L , R , c , lson);
if (m < R) update(L , R , c , rson);
PushUp(rt);
}
LL query(int L,int R,int l,int r,int rt) {
if (L <= l && r <= R) {
return sum[rt];
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
LL ret = 0;
if (L <= m) ret += query(L , R , lson);
if (m < R) ret += query(L , R , rson);
return ret;
}
ll query_path(int x, int y)
{
ll ans = 0;
int fx = top[x], fy = top[y];
while (fx != fy) {
if (deep[fx] >= deep[fy]) {
ans += query(p[fx],p[x],1,n,1);
x = fa[fx];
}
else {
ans += query(p[fy],p[y],1,n,1);
y = fa[fy];
}
fx = top[x], fy = top[y];
}
if (x != y)
{
if (p[x] < p[y])
{
ans += query(p[x],p[y],1,n,1);
}
else
{
ans += query(p[y],p[x],1,n,1);
}
}
else ans += query(p[x],p[y],1,n,1);
return ans;
}
int main()
{
int m;
int u,v,op,x;
ll tem;
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
dfs1(1, 0, 0);
dfs2(1,1);
build(1,n,1);
while(m--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d%lld",&x,&tem);
update(p[x],p[x],tem,1,n,1);
}
else if(op==2)
{
scanf("%d%lld",&x,&tem);
update(p[x],mx[x],tem,1,n,1);
}
else if(op==3)
{
scanf("%d",&x);
printf("%lld\n",query_path(1,x));
}
}
return 0;
}