[BZOJ3730]震波

Description

在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i]。
不幸的是,这片土地常常发生地震,并且随着时代的发展,城市的价值也往往会发生变动。
接下来你需要在线处理M次操作:
0 x k 表示发生了一次地震,震中城市为x,影响范围为k,所有与x距离不超过k的城市都将受到影响,该次地震造成的经济损失为所有受影响城市的价值和。
1 x y 表示第x个城市的价值变成了y。
为了体现程序的在线性,操作中的x、y、k都需要异或你程序上一次的输出来解密,如果之前没有输出,则默认上一次的输出为0。

Input

第一行包含两个正整数N和M。
第二行包含N个正整数,第i个数表示value[i]。
接下来N-1行,每行包含两个正整数u、v,表示u和v之间有一条无向边。
接下来M行,每行包含三个数,表示M次操作。

Output

包含若干行,对于每个询问输出一行一个正整数表示答案。

Sample Input

8 1
1 10 100 1000 10000 100000 1000000 10000000
1 2
1 3
2 4
2 5
3 6
3 7
3 8
0 3 1

Sample Output

11100101

HINT

1<=N,M<=100000

1<=u,v,x<=N

1<=value[i],y<=10000

0<=k<=N-1


题解

点分树+线段树

刚刚学习的点分树

发现是一个神的数据结构

点分树就是把树重构,使原树变成一个树高为log的东西

这样在上面暴跳\(fa[]\)之类的操作就变得合理了

这道题要求我们支持修改点权和查询距离一个点不超过k的点权和

可以对原树建点分树

然后开线段树维护相关信息

这类问题有个套路

就是因为我们查询和修改时要暴跳\(fa[]\)

所以在统计\(fa[]\)的信息时需要容斥掉当前子树内的相关信息

这样我们对于每个点就要开两棵线段树

一棵线段树rt1表示点u的子树内到u距离小于等于k的点的权值和

另外一棵线段树rt2表示点u的子树内到\(fa[u]\)的距离小于等于k的点权值和

这样我们暴跳统计信息的时候就可以直接用\(rt1[fa_u] - rt2[u]\)来容斥掉该子树内的信息了

然后还有这题卡常然而我常数肥肠大就煤过==

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define ls t[now].l
# define rs t[now].r
const int M = 100005 ;
const int INF = 1e9 + 7 ;
using namespace std ;
inline int read() {
	char c = getchar() ; int x = 0 , w = 1 ;
	while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
	while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
	return x*w ;
}

bool vis[M] ;
int n , num , hea[M] ;
int dep[M] , st[M][20] , val[M] ;
int tot , tmin = INF , root , size[M] ;
int cnt , fa[M] , rt1[M] , rt2[M] ;
struct E {
	int Nxt , to ;
} edge[M << 1] ;
struct Node {
	int l , r , Sum ;
} t[M << 7] ;
inline void add_edge(int from , int to) {
	edge[++num].Nxt = hea[from] ;
	edge[num].to = to ; hea[from] = num ;
}

void Dfs(int u , int father) {
	st[u][0] = father ;
	for(int i = hea[u] ; i ; i = edge[i].Nxt) {
		int v = edge[i].to ; if(vis[v] || v == father) continue ;
		dep[v] = dep[u] + 1 ; Dfs(v , u) ; 
	}
}
inline void ST() {
	for(int j = 1 ; j <= 17 ; j ++)
	    for(int i = 1 ; i <= n ; i ++)
	        st[i][j] = st[st[i][j - 1]][j - 1] ;
}
inline int Gdis(int u , int v) {
	int ret = 0 ; if(dep[u] < dep[v]) swap(u , v) ;
	for(int i = 17 ; i >= 0 ; i --)
	    if(dep[st[u][i]] >= dep[v]) {
	    	ret += dep[u] - dep[st[u][i]] ;
	    	u = st[u][i] ;
		}
	if(u == v) return ret ;
	for(int i = 17 ; i >= 0 ; i --) 
	    if(st[u][i] != st[v][i]) {
	    	ret += (dep[u] - dep[st[u][i]]) + (dep[v] - dep[st[v][i]]) ;
	    	u = st[u][i] , v = st[v][i] ;
		}
	return ret + (dep[u] - dep[st[u][0]]) + (dep[v] - dep[st[v][0]]) ;
}
void Getroot(int u , int father) {
	size[u] = 1 ; int Mx = -1 ;
	for(int i = hea[u] ; i ; i = edge[i].Nxt) {
		int v = edge[i].to ; if(v == father || vis[v]) continue ;
		Getroot(v , u) ; size[u] += size[v] ; Mx = max(Mx , size[v]) ;
	}
	Mx = max(Mx , tot - size[u]) ; if(Mx < tmin) tmin = Mx , root = u ;
}
void Solve(int u , int father) {
	fa[u] = father ; vis[u] = true ;
	for(int i = hea[u] ; i ; i = edge[i].Nxt) {
		int v = edge[i].to ; if(vis[v]) continue ;
		tot = size[v] ; tmin = INF ;
		Getroot(v , u) ; Solve(root , u) ;
	}
}
inline void pushup(int now) {
	t[now].Sum = t[ls].Sum + t[rs].Sum ;
}
void Insert(int x , int v , int l , int r , int &now) {
	if(!now) now = ++cnt ;
	if(l == r) { t[now].Sum += v ; return ; }
	int mid = (l + r) >> 1 ;
	if(mid >= x) Insert(x , v , l , mid , ls) ;
	else Insert(x , v , mid + 1 , r , rs) ;
	pushup(now) ;
}
int query(int L , int R , int l , int r , int now) {
	if(l > R || r < L) return 0 ;
	if(!now) return 0 ; if(l == L && r == R) return t[now].Sum ;
	int mid = (l + r) >> 1 ;
	if(mid >= R) return query(L , R , l , mid , ls) ;
	else if(mid < L) return query(L , R , mid + 1 , r , rs) ;
	else return query(L , mid , l , mid , ls) + query(mid + 1 , R , mid + 1 , r , rs) ;
}
void Change(int x,int v) {
	Insert(0 , v , 0 , n , rt1[x]) ;
	for(int u = x , dis1 ; fa[u] ; u = fa[u]) {
		dis1 = Gdis(x , fa[u]) ;
		Insert(dis1 , v , 0 , n , rt1[fa[u]]) ;
	    Insert(dis1 , v , 0 , n , rt2[u]) ;
	}
}
int main() {
	n = read() ; int Q = read() ;
	for(int i = 1 ; i <= n ; i ++) val[i] = read() ;
	for(int i = 1 , u , v ; i < n ; i ++) {
		u = read() , v = read() ;
		add_edge(u , v) ; add_edge(v , u) ;
	}
	Dfs(1 , 0) ; ST() ;
	tot = n ; Getroot(1 , 1) ; Solve(root , 0) ;
	for(int x = 1 ; x <= n ; x ++) Change(x,val[x]);
	int opt , x , k , Ans = 0 , dlt ;
	while(Q --) {
		opt = read() , x = read() , k = read() ; x ^= Ans ; k ^= Ans ;
		if(opt == 1) {
			dlt = k - val[x] ;
			Change(x , dlt) ; val[x] = k ;
 		}
		else {
			Ans = query(0 , k , 0 , n , rt1[x]) ;
			for(int i = x , dis1 ; fa[i] ; i = fa[i]) {
				dis1 = Gdis(x , fa[i]) ;
				if(k < dis1) continue ;
				Ans += query(0 , k - dis1 , 0 , n , rt1[fa[i]]) ;
				Ans -= query(0 , k - dis1 , 0 , n , rt2[i]) ;
			}
			printf("%d\n",Ans) ;
		}
	}
	return 0 ;
}
posted @ 2018-12-04 16:12  beretty  阅读(167)  评论(0编辑  收藏  举报