bzoj4034(dfs序+BIT/链剖+线段树)

16 篇文章 0 订阅
10 篇文章 0 订阅

记得以前是用链剖+线段树做的,在链剖的时候顺便维护dfs序,思路简单实现起来就有点麻烦了。。

可参照:http://www.cnblogs.com/onlyRP/p/5041702.html

回顾这题发现其实主要是2操作针对子树而3操作针对链,而且唯一的询问操作3的链其实是到根的权值和,那么就想能不能直接dfs序进行操作呢?做一遍dfs序后我们可以用前缀和求出权值和,操作1修改点权值就把贡献限制在子树内就可以,然而修改树权值就感觉没什么思路了。。操作2对每个结点贡献是t*(d[x]-d[f]),也就是每个结点修改的还不一样,无法统一修改。。

上网找题解发现可以用2个BIT维护,对子树中的结点贡献还可以化为t*d[x]-t*d[f],由于t*d[f]恒定,所以可以类似操作1直接修改点权值,剩下的就再开一个BIT维护_t就可以了,询问的时候直接乘上d[x]即可。。



#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define inf 1000000007
#define eps 1e-8
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define ls T[i<<1]
#define rs T[i<<1|1]
#define op T[i]
#define NM 100005
#define nm 200005
#define pi 3.141592653
using namespace std;
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}


struct edge{int t;edge*next;}e[nm],*h[NM],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}
ll a[2][NM],_y;
int in[NM],out[NM],d[NM],tot;
int n,m,_x,_t,b[NM];
void mod(int x,int k,ll t){while(x<=n)a[k][x]+=t,x+=lowbit(x);}
ll sum(int x,int k){return x?a[k][x]+sum(x-lowbit(x),k):0;}

void dfs(int x){
	in[x]=++tot;
	link(x)if(!in[j->t]){d[j->t]=d[x]+1;dfs(j->t);}
	out[x]=tot;
}

int main(){
	freopen("data.in","r",stdin);
	n=read();m=read();
	inc(i,1,n)b[i]=read();
	inc(i,1,n-1){_x=read();_y=read();add(_x,_y);add(_y,_x);}
	dfs(1);
	inc(i,1,n)mod(in[i],0,b[i]),mod(out[i]+1,0,-b[i]);
	while(m--){
		_t=read();_x=read();
		if(_t==3)printf("%lld\n",sum(in[_x],0)+sum(in[_x],1)*(d[_x]+1));
		else{
			_y=read();
			if(_t==1)mod(in[_x],0,_y),mod(out[_x]+1,0,-_y);
			else{
				mod(in[_x],1,_y);mod(out[_x]+1,1,-_y);
				mod(in[_x],0,-_y*d[_x]);mod(out[_x]+1,0,_y*d[_x]);
			}
		}
	}
	return 0;
}


4034: [HAOI2015]树上操作

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 6230   Solved: 2061
[ 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

HINT

 对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。

Source

[ Submit][ Status][ Discuss]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值