[BZOJ]3730 震波 动态点分治&树状数组

3730: 震波

Time Limit: 15 Sec   Memory Limit: 256 MB
Submit: 1641   Solved: 376
[ Submit][ Status][ Discuss]

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

Source

[ Submit][ Status][ Discuss]


HOME Back

  辣鸡题目!! 卡我常数!! 别人用的还是线段树而我tm用的是常数小的树状数组!! 我都T了!! 气死我也. 感觉主要是resize和找lca用的是倍增的锅. 幸好之所以T是因为当时有四个人同时评测, 再交一遍就A了... 而且tm还能在100名左右... 想起之前因为同时评测还T了感觉荒谬. 想起来让以前自称代码自带小常数的我无地自容. 准备过几天花一个上午学习一下常数相关. 

  做法还是比较naive. 点分治之后每个点维护一个树状数组以距离为下标, bit[u][i]表示当前u的分治子树里距离u节点距离为i的点的权值和. 显然要查询距离<=k的话直接用树状数组搞一搞就行了, 由于要保证不mle, 所以要每个点由当前分治size来resize自己的bit数组, 开个vector就好了. 由重心及点分治的性质易得最后空间是nlogn的. 那么修改的时候直接顺着点分树往上爬修改就好了. 查询的时候在u当前查询k, 在fa[u]那里查询k - dis(u, fa[u])(画画图就明白了),然后发现fa[u]里计算到的有些点也存在于u的分治子树中, 然而这个我们已经在u的时候计算过了, 要去一下重. 维护一个fbit[u][i]表示u的分治子树里距离u节点的分治父亲距离为i的点的权值和, 那么去重的话就-fbit[u][k - dis(u, fa[u])就好辣. fbit在修改的时候与bit一起维护, 然后往上爬就以此类推辣. 注意询问的是<=k所以自己也算(在查询是树状数组是以1为下标开始的, 没算0... 哎呀详见代码辣).

  之前d < 0的时候就没有更新last re了好久, 实际上是wa... 这种题最烦人的就是在线异或一旦中间错误很容易re而不反馈wa的结果...

  一个细节: 如果当前调到的点v, k - dis(u, v) < 0的话要continue而不是break. 你到你爷爷的距离远, 但是你的祖父有可能就在你旁边.(画个图就知道了, 也比较显然...

#include<bits/stdc++.h>
using namespace std;
const int P = 20;
const int inf = 1e9;
const int maxn = 1e5 + 5;
bool vis[maxn];
int n, m, num, mn, rt, size, ans;
vector<int> bit[maxn], fbit[maxn];
int h[maxn], dep[maxn], value[maxn], fa[maxn], siz[maxn], pw[P], anc[maxn][P + 1];
inline const int read() {
	register int x = 0;
	register char ch = getchar();
	while (ch < '0' || ch > '9') ch = getchar();
	while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return x;
}
struct edge {
	int nxt, v;
}e[maxn << 1];
inline void add(int u, int v) {
	e[++ num].v = v, e[num].nxt = h[u], h[u] = num;
	e[++ num].v = u, e[num].nxt = h[v], h[v] = num;
}
inline int lca(int u, int v) {
	if (dep[v] > dep[u]) swap(u, v);
	int t = dep[u] - dep[v];
	for (int i = 0; i < P; ++ i)
		if (t & pw[i]) u = anc[u][i];
	for (int i = P - 1; ~i && u != v; -- i)
		if (anc[u][i] != anc[v][i])
			u = anc[u][i], v = anc[v][i];
	return (u == v) ? u : anc[u][0];
}
inline int querydis(int u, int v) {
	return dep[u] + dep[v] - (dep[lca(u, v)] << 1);
}
void dfs(int u, int f) {
	anc[u][0] = f;
	for (int i = 1; pw[i] <= dep[u]; ++ i)
		anc[u][i] = anc[anc[u][i - 1]][i - 1];
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (v == f) continue;
		dep[v] = dep[u] + 1;
		dfs(v, u);
	}
}
void dfsrt(int u, int tf) {
	siz[u] = 1;
	int mx = 0;
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (v == tf || vis[v]) continue;
		dfsrt(v, u);
		siz[u] += siz[v];
		if (mx < siz[v]) mx = siz[v];
	}
	mx = max(mx, size - siz[u]);
	if (mx < mn) rt = u, mn = mx;
}
void divi(int u, int df) {
	vis[u] = true, fa[u] = df;
	bit[u].resize(size + 1), fbit[u].resize(size + 1);
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (vis[v]) continue;
		rt = 0, mn = inf, size = siz[v];
		dfsrt(v, u), divi(rt, u);
	}
}
inline void update(int u, int val) {
	if (u <= 0) return;
	for (int df = u, ff, dis, lim; df; df = fa[df]) {
		lim = bit[df].size() - 1;
		dis = querydis(u, df);
		for (; dis <= lim && dis; dis += dis & -dis) bit[df][dis] += val;
		if (!(ff = fa[df])) break;
		dis = querydis(u, ff);
		for (; dis <= lim && dis; dis += dis & -dis) fbit[df][dis] += val; 
	}
}
inline int query(int u, int k) {
	int sum = value[u], lim = bit[u].size() - 1, last = u;
	lim = min(k, lim);
	for (int i = lim; i; i -= i & -i) sum += bit[u][i];
	for (int df = fa[u], dis; df; last = df, df = fa[df]) {
		int d = k - querydis(u, df);
		if (d < 0) continue;
		lim = bit[df].size() - 1, lim = min(d, lim);
		for (dis = lim; dis; dis -= dis & -dis) sum += bit[df][dis];
		lim = fbit[last].size() - 1, lim = min(d, lim);
		for (dis = lim; dis; dis -= dis & -dis) sum -= fbit[last][dis];
		sum += value[df];
	}
	return sum;
}
int main() {
	pw[0] = 1;
	for (int i = 1; i < P; ++ i) pw[i] = pw[i - 1] << 1;
	n = read(), m = read();
	for (int i = 1; i <= n; ++ i) value[i] = read();
	for (int i = 1, u, v; i < n; ++ i)
		u = read(), v = read(), add(u, v);
	dfs(1, 0);
	mn = inf, rt = 0, size = n, dfsrt(1, 0), divi(rt, 0);
	for (int i = 1; i <= n; ++ i) update(i, value[i]);
	for (int i = 1, opt, x, y; i <= m; ++ i) {
		opt = read(), x = read(), y = read();
		x ^= ans, y ^= ans;
		if (!opt) printf("%d\n", ans = query(x, y));
		else update(x, y - value[x]), value[x] = y;
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值