题目描述
在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i]。
不幸的是,这片土地常常发生地震,并且随着时代的发展,城市的价值也往往会发生变动。
接下来你需要在线处理M次操作:
0 x k 表示发生了一次地震,震中城市为x,影响范围为k,所有与x距离不超过k的城市都将受到影响,该次地震造成的经济损失为所有受影响城市的价值和。
1 x y 表示第x个城市的价值变成了y。
为了体现程序的在线性,操作中的x、y、k都需要异或你程序上一次的输出来解密,如果之前没有输出,则默认上一次的输出为0。
输入
第一行包含两个正整数N和M。
第二行包含N个正整数,第i个数表示value[i]。
接下来N-1行,每行包含两个正整数u、v,表示u和v之间有一条无向边。
接下来M行,每行包含三个数,表示M次操作。
输出
包含若干行,对于每个询问输出一行一个正整数表示答案。
样例输入
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
样例输出
11100101
题解
动态点分治+线段树
考虑点分树的某一层除了查询点对应子树的部分,它们到查询点的路径都是:该点-该层节点-查询点。
因此求出该层节点到查询点的距离,就知道了该点到该层节点的距离的范围。
于是可以对于每一个节点按照距离维护一棵线段树,表示该节点点分树子树内到其距离为某值的所有节点的权值和。
一个问题:如何表示点分树除了查询点子树的部分?
静态点分治提供了一个很好的方法:容斥。
因此还要对每个节点维护另一颗线段树,表示该节点点分树子树内到其父亲节点距离为某值的所有节点的权值和。查询时减一下即可。
另一个问题:如何快速求出两点间距离?使用RMQLCA,预处理后 $O(1)$ 查询LCA深度即可。
时间复杂度 $O(n\log^2n)$
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
#define lson l , mid , ls[x]
#define rson mid + 1 , r , rs[x]
using namespace std;
int n , head[N] , to[N << 1] , next[N << 1] , cnt , deep[N] , pos[N] , md[20][N << 1] , log[N << 1] , tot;
int v[N] , vis[N] , si[N] , ms[N] , ts , root , fa[N] , ra[N] , rb[N];
int ls[N * 200] , rs[N * 200] , sum[N * 200] , tp;
inline void add(int x , int y)
{
to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x , int last)
{
int i;
pos[x] = ++tot , md[0][tot] = deep[x];
for(i = head[x] ; i ; i = next[i])
if(to[i] != last)
deep[to[i]] = deep[x] + 1 , dfs(to[i] , x) , md[0][++tot] = deep[x];
}
inline int dis(int x , int y)
{
int t = deep[x] + deep[y] , k;
x = pos[x] , y = pos[y];
if(x > y) swap(x , y);
k = log[y - x + 1];
return t - (min(md[k][x] , md[k][y - (1 << k) + 1]) << 1);
}
void update(int p , int a , int l , int r , int &x)
{
if(!x) x = ++tp;
sum[x] += a;
if(l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) update(p , a , lson);
else update(p , a , rson);
}
int query(int p , int l , int r , int x)
{
if(!x) return 0;
if(l == r) return sum[x];
int mid = (l + r) >> 1;
if(p <= mid) return query(p , lson);
else return query(p , rson) + sum[ls[x]];
}
void getroot(int x , int last)
{
int i;
si[x] = 1 , ms[x] = 0;
for(i = head[x] ; i ; i = next[i])
if(!vis[to[i]] && to[i] != last)
getroot(to[i] , x) , si[x] += si[to[i]] , ms[x] = max(ms[x] , si[to[i]]);
ms[x] = max(ms[x] , ts - si[x]);
if(ms[x] < ms[root]) root = x;
}
void deal(int x , int last , int p)
{
update(dis(x , p) , v[x] , 0 , n - 1 , ra[p]);
if(fa[p]) update(dis(x , fa[p]) , v[x] , 0 , n - 1 , rb[p]);
int i;
si[x] = 1;
for(i = head[x] ; i ; i = next[i])
if(!vis[to[i]] && to[i] != last)
deal(to[i] , x , p) , si[x] += si[to[i]];
}
void work(int x)
{
int i;
deal(x , 0 , x) , vis[x] = 1;
for(i = head[x] ; i ; i = next[i])
if(!vis[to[i]])
root = 0 , ts = si[to[i]] , getroot(to[i] , 0) , fa[root] = x , work(root);
}
inline void modify(int x , int y)
{
int i;
for(i = x ; i ; i = fa[i])
{
update(dis(x , i) , y - v[x] , 0 , n - 1 , ra[i]);
if(fa[i]) update(dis(x , fa[i]) , y - v[x] , 0 , n - 1 , rb[i]);
}
v[x] = y;
}
inline int solve(int x , int k)
{
int i , ans = 0;
for(i = x ; i ; i = fa[i])
{
if(dis(x , i) <= k) ans += query(k - dis(x , i) , 0 , n - 1 , ra[i]);
if(fa[i] && dis(x , fa[i]) <= k) ans -= query(k - dis(x , fa[i]) , 0 , n - 1 , rb[i]);
}
return ans;
}
int main()
{
int m , i , j , opt , x , y , last = 0;
scanf("%d%d" , &n , &m);
for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &v[i]);
for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x);
dfs(1 , 0);
for(i = 2 ; i <= tot ; i ++ ) log[i] = log[i >> 1] + 1;
for(i = 1 ; (1 << i) <= tot ; i ++ )
for(j = 1 ; j <= tot - (1 << i) + 1 ; j ++ )
md[i][j] = min(md[i - 1][j] , md[i - 1][j + (1 << (i - 1))]);
ms[0] = 1 << 30 , ts = n , getroot(1 , 0) , work(root);
while(m -- )
{
scanf("%d%d%d" , &opt , &x , &y) , x ^= last , y ^= last;
if(!opt) printf("%d\n" , last = solve(x , y));
else modify(x , y);
}
return 0;
}