[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 ;
}