bzoj4034 (树链剖分)

题目链接: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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值