BZOJ4372: 烁烁的游戏【动态点分治】

Description

背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠。

题意:

给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠。

烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠。皮皮鼠会被烁烁吸引,所以会一直待在节点上不动。

烁烁很好奇,在当前时刻,节点u有多少个他的好朋友---皮皮鼠。

大意:

给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:

Q x:询问x的点权。

M x d w:将树上与节点x距离不超过d的节点的点权均加上w。

Input

第一行两个正整数:n,m

接下来的n-1行,每行三个正整数u,v,代表u,v之间有一条边。

接下来的m行,每行给出上述两种操作中的一种。

Output

对于每个Q操作,输出当前x节点的皮皮鼠数量。

Sample Input

7 6

1 2

1 4

1 5

2 3

2 7

5 6

M 1 1 2

Q 5

M 2 2 3

Q 3

M 1 2 1

Q 2

Sample Output

2

3

6

HINT

数据范围:

\(n,m<=10^5,|w|<=10^4\)

注意:w不一定为正整数,因为烁烁可能把皮皮鼠吓傻了。


思路

首先我们就发现操作是把距离到一个点不超过k的点权值加上一个w,然后查询一个点的权值

首先我们就发现对于每个点可以算出来距离小于等于k的点被加上了多少

然后查询的时候我们就只需要看每个节点和查询节点的距离在那个节点上被添加了多少次就可以了

这个过程是可以用动态点分治来优化的

可以用欧拉序处理掉LCA,然后因为每次我们都是区间加,但点查询,所以就可以用树状数组来维护了

但是注意树状数组的大小需要动态控制


注意下细节就可以了


#include<bits/stdc++.h>

using namespace std;

int read() {
int res = 0, w = 1; char c = getchar();
while (!isdigit(c) && c != '-') c = getchar();
if (c == '-') w = -1, c = getchar();
while (isdigit(c)) res = (res << 1) + (res << 3) + c - '0', c = getchar();
return res * w;
} const int N = 1e5 + 10;
const int LOG = 20; struct Edge {
int v, nxt;
Edge(int v = 0, int nxt = 0): v(v), nxt(nxt) {}
} E[N << 1];
int head[N], tot = 0;
int n, q;
char c[10]; void addedge(int u, int v) {
E[++tot] = Edge(v, head[u]);
head[u] = tot;
} namespace LCA { struct Node {
int id, depth;
Node(int id = 0, int depth = 0): id(id), depth(depth) {}
bool operator < (const Node b) const {
return depth < b.depth;
}
} ST[N << 1][LOG];
int first[N], dep[N], log[N << 1], len; void dfs(int u, int fa) {
dep[u] = dep[fa] + 1;
ST[++len][0] = Node(u, dep[u]);
first[u] = len;
for (int i = head[u]; i; i = E[i].nxt) {
int v = E[i].v;
if (v == fa) continue;
dfs(v, u);
ST[++len][0] = Node(u, dep[u]);
}
} void init() {
dfs(1, 0);
log[1] = 0;
for (int i = 2; i <= len; i++) log[i] = log[i >> 1] + 1;
for (int j = 1; (1 << j) <= len; j++) {
for (int i = 1; i + (1 << j) - 1 <= len; i++) {
ST[i][j] = min(ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);
}
}
} int getdis(int u, int v) {
if (first[u] < first[v]) swap(u, v);
int k = log[first[u] - first[v] + 1];
int lca = min(ST[first[v]][k], ST[first[u] - (1 << k) + 1][k]).id;
return dep[u] + dep[v] - (dep[lca] << 1);
} } namespace Tree_Devide { int father[N], dep[N], maxdep;
int siz[N], F[N], siz_all, rt;
bool vis[N];
vector<int> bit[2][N]; void modify(int x, int vl, int typ, int u) {
int len = bit[typ][u].size();
while (x < len) {
bit[typ][u][x] += vl;
x += x & (-x);
}
} void modify(int l, int r, int vl, int typ, int u) {
modify(l + 1, vl, typ, u);
modify(r + 2, -vl, typ, u);
} int query(int x, int typ, int u) {
int len = bit[typ][u].size(), res = 0;
x = min(x + 1, len - 1);
while (x) {
res += bit[typ][u][x];
x -= x & (-x);
}
return res;
} void getsiz(int u, int fa) {
siz[u] = 1;
for (int i = head[u]; i; i = E[i].nxt) {
int v = E[i].v;
if (v == fa || vis[v]) continue;
dep[v] = dep[u] + 1;
maxdep = max(maxdep, dep[v]);
getsiz(v, u);
siz[u] += siz[v];
}
} void getroot(int u, int fa) {
F[u] = 0;
dep[u] = dep[fa] + 1;
maxdep = max(maxdep, dep[u]);
for (int i = head[u]; i; i = E[i].nxt) {
int v = E[i].v;
if (v == fa || vis[v]) continue;
getroot(v, u);
F[u] = max(F[u], siz[v]);
}
F[u] = max(F[u], siz_all - siz[u]);
if (F[u] < F[rt]) rt = u;
} void solve(int u, int fa) {
father[u] = fa;
vis[u] = 1;
maxdep = dep[u] = 0;
getsiz(u, 0);
bit[0][u].resize(maxdep + 4);
for (int i = head[u]; i; i = E[i].nxt) {
int v = E[i].v;
if (vis[v]) continue;
F[rt = 0] = siz_all = siz[v];
maxdep = 0;
getroot(v, 0);
bit[1][rt].resize(maxdep + 4);
solve(rt, u);
}
} void init() {
getsiz(1, 0);
F[rt = 0] = siz_all = n;
getroot(1, 0);
solve(rt, 0);
} void modify_tree(int u, int k, int num) {
modify(0, k, num, 0, u);
for (int cur = u; father[cur]; cur = father[cur]) {
int dis = LCA::getdis(u, father[cur]);
if (k >= dis) {
modify(0, k - dis, num, 0, father[cur]);
modify(0, k - dis, num, 1, cur);
}
}
} int query_tree(int u) {
int res = query(0, 0, u);
for (int cur = u; father[cur]; cur = father[cur]) {
int dis = LCA::getdis(u, father[cur]);
res += query(dis, 0, father[cur]);
res -= query(dis, 1, cur);
}
return res;
} } int main() {
#ifdef dream_maker
freopen("input.txt", "r", stdin);
#endif
n = read(), q = read();
for (int i = 1; i < n; i++) {
int u = read(), v = read();
addedge(u, v);
addedge(v, u);
}
LCA::init();
Tree_Devide::init();
while (q--) {
scanf("%s", c);
if (c[0] == 'M') {
int u = read(), k = read(), num = read();
Tree_Devide::modify_tree(u, k, num);
} else {
int u = read();
printf("%d\n", Tree_Devide::query_tree(u));
}
}
return 0;
}
上一篇:SVN Hook造成SVN提交速度慢的问题


下一篇:JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查