有一棵点数为 的树,以点 为根,且树点有边权。然后有 个操作,分为三种:
- 把某个节点 的点权增加 。
- 把某个节点 为根的子树中所有点的点权都增加 。
- 询问某个节点 到根的路径中所有点的点权和。
链接
题解
裸树剖 + DFS 序,注意要开 long long
,要不然和暴力分一样 ……
代码
#include <cstdio>
#include <stack>
const int MAXN = 100000;
const int MAXM = 100000;
struct Node;
struct Edge;
struct Path;
struct Node {
Edge *e;
Node *c, *p;
int size, pos, posEnd;
bool visited;
Path *path;
} N[MAXN];
struct Edge {
Node *s, *t;
Edge *next;
Edge(Node *s, Node *t) : s(s), t(t), next(s->e) {}
};
inline void addEdge(const int u, const int v) {
N[u].e = new Edge(&N[u], &N[v]);
N[v].e = new Edge(&N[v], &N[u]);
}
struct SegmentTree {
int l, r;
SegmentTree *lc, *rc;
long long value, lazy;
SegmentTree(const int l, const int r, SegmentTree *lc, SegmentTree *rc) : l(l), r(r), lc(lc), rc(rc), value(0), lazy(0) {}
void cover(const long long delta) { value += delta * (r - l + 1), lazy += delta; }
void pushDown() {
if (lazy) {
if (lc) lc->cover(lazy);
if (rc) rc->cover(lazy);
lazy = 0;
}
}
void update(const int l, const int r, const long long delta) {
if (l > this->r || r < this->l) return;
else if (l <= this->l && r >= this->r) cover(delta);
else {
pushDown();
value = 0;
if (lc) lc->update(l, r, delta), value += lc->value;
if (rc) rc->update(l, r, delta), value += rc->value;
}
}
long long query(const int l, const int r) {
if (l > this->r || r < this->l) return 0;
else if (l <= this->l && r >= this->r) return value;
else {
pushDown();
long long result = 0;
if (lc) result += lc->query(l, r);
if (rc) result += rc->query(l, r);
return result;
}
}
} *segment;
SegmentTree *buildSegmentTree(const int l, const int r) {
if (l > r) return NULL;
else if (l == r) return new SegmentTree(l, r, NULL, NULL);
else return new SegmentTree(l, r, buildSegmentTree(l, l + ((r - l) >> 1)), buildSegmentTree(l + ((r - l) >> 1) + 1, r));
}
struct Path {
Node *top;
Path(Node *top) : top(top) {}
};
int n, m;
long long a[MAXN];
inline void cut() {
std::stack<Node *> s;
s.push(&N[0]);
while (!s.empty()) {
Node *v = s.top();
if (!v->visited) {
v->visited = true;
for (Edge *e = v->e; e; e = e->next) if (e->t->p == NULL && e->t != v->p) e->t->p = v, s.push(e->t);
} else {
v->size = 1;
for (Edge *e = v->e; e; e = e->next) if (e->t->p == v) {
v->size += e->t->size;
if (v->c == NULL || v->c->size < e->t->size) v->c = e->t;
}
s.pop();
}
}
for (int i = 0; i < n; i++) N[i].visited = false;
s.push(&N[0]);
int time = -1;
while (!s.empty()) {
Node *v = s.top();
if (!v->visited) {
v->visited = true;
v->pos = ++time;
if (!v->p || v != v->p->c) v->path = new Path(v);
else v->path = v->p->path;
for (Edge *e = v->e; e; e = e->next) if (e->t->p == v && e->t != v->c) s.push(e->t);
if (v->c) s.push(v->c);
} else v->posEnd = time, s.pop();
}
segment = buildSegmentTree(0, n - 1);
for (int i = 0; i < n; i++) segment->update(N[i].pos, N[i].pos, a[i]);
}
inline long long queryToRoot(const int x) {
Node *v = &N[x];
long long sum = 0;
while (v) {
sum += segment->query(v->path->top->pos, v->pos);
v = v->path->top->p;
}
return sum;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
for (int i = 0, u, v; i < n - 1; i++) scanf("%d %d", &u, &v), u--, v--, addEdge(u, v);
cut();
for (int i = 0, cmd; i < m; i++) {
scanf("%d", &cmd);
if (cmd == 1) {
int x, a;
scanf("%d %d", &x, &a), x--;
segment->update(N[x].pos, N[x].pos, a);
} else if (cmd == 2) {
int x, a;
scanf("%d %d", &x, &a), x--;
segment->update(N[x].pos, N[x].posEnd, a);
} else if (cmd == 3) {
int x;
scanf("%d", &x), x--;
printf("%lld\n", queryToRoot(x));
}
}
return 0;
}