ACM-ICPC 2018 焦作賽區網路預賽 E 樹鏈剖分
阿新 • • 發佈:2018-12-10
題意:給定一棵以為根的節點,存在路徑加,路徑乘,路徑的節點權值取反,查詢路徑和等四個操作。
思路:
典型的樹鏈剖分題目。
對於取反,可以轉化成減法操作,比如:
00011取反為:11100
等價於 (11111 - 00011) = 11100
即將原值乘上(-1),再加上 , 為二進位制的位數
這樣就可以把取反操作轉化為加法和乘法操作。
對於路徑加和乘法的維護,可以使用兩個變數add, mul。
則 線段樹的節點值為:
當
更新為時,則
更新為時,則 且
此題得解。
程式碼:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef unsigned long long ull;
#define lson rt<<1
#define rson rt<<1|1
const int A = 2e5 + 10;
class Gra{
public :
int v,next;
}G[A<<2];
class Seg_Tree{
public:
int l,r,len;
ull sum, add, mul;
}Tree[A<<2];
int n, m, head[A], tot, twt;
int fa[A], dep[A], siz[A], son[A], top[A], pos[A], ID[A];
ull Val;
void Init(){
for (int i = 0; i < A; i++) head[i] = -1;
tot = twt = 0;
}
void add(int u,int v){
G[tot].v = v;
G[tot].next = head[u];
head[u] = tot++;
}
void dfs(int u, int pre, int d){
fa[u] = pre;son[u] = -1;
siz[u] = 1;dep[u] = d;
for (int i = head[u]; i != -1; i=G[i].next) {
int v = G[i].v;
if (v == pre) continue;
dfs(v, u, d + 1);
siz[u] += siz[v];
if (son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
}
}
void dfs(int u, int tp){
pos[u] = ++twt;
ID[twt] = u;
top[u] = tp;
if (son[u] == -1) return;
dfs(son[u], tp);
for (int i = head[u]; i != -1; i = G[i].next) {
int v = G[i].v;
if(v == son[u] || v == fa[u]) continue;
dfs(v,v);
}
}
void push_up(int rt){
Tree[rt].sum = Tree[lson].sum + Tree[rson].sum;
}
void push_down(int rt){
if (Tree[rt].mul != 1) {
Tree[lson].mul *= Tree[rt].mul;
Tree[rson].mul *= Tree[rt].mul;
Tree[lson].add *= Tree[rt].mul;
Tree[rson].add *= Tree[rt].mul;
Tree[lson].sum *= Tree[rt].mul;
Tree[rson].sum *= Tree[rt].mul;
Tree[rt].mul = 1;
}
if (Tree[rt].add) {
Tree[lson].add += Tree[rt].add;
Tree[rson].add += Tree[rt].add;
Tree[lson].sum += Tree[lson].len * Tree[rt].add;
Tree[rson].sum += Tree[rson].len * Tree[rt].add;
Tree[rt].add = 0;
}
}
void build_Tree(int rt,ull l,ull r){
Tree[rt].l = l;Tree[rt].r = r;
Tree[rt].len = r - l + 1;
Tree[rt].sum = Tree[rt].add = 0;
Tree[rt].mul = 1;
if (l == r) return;
ull mid = (l+r)/2;
build_Tree(lson, l, mid);
build_Tree(rson, mid + 1, r);
push_up(rt);
}
void update_Tree(int rt, ull st, ull ed, ull c, int id){
ull l = Tree[rt].l, r = Tree[rt].r;
if (st <= l && r <= ed) {
push_down(rt);
if (id) {
Tree[rt].sum += Tree[rt].len * c;
Tree[rt].add += c;
} else {
Tree[rt].sum *= c;
Tree[rt].add *= c;
Tree[rt].mul *= c;
}
return;
}
push_down(rt);
ull mid = (l + r)/2;
if (st <= mid) update_Tree(lson, st, ed, c, id);
if (ed > mid) update_Tree(rson, st, ed, c, id);
push_up(rt);
}
ull query_Tree(int rt, ull st, ull ed){
ull l = Tree[rt].l, r = Tree[rt].r;
if (st <= l && r <= ed) {
push_down(rt);
return Tree[rt].sum;
}
push_down(rt);
ull mid = (l + r)/2;
ull res = 0;
if (st <= mid) res += query_Tree(lson, st, ed);
if (ed > mid) res += query_Tree(rson, st, ed);
push_up(rt);
return res;
}
void calc(int x, int y, ull c, int id) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x,y);
update_Tree(1, pos[top[x]], pos[x], c, id);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x,y);
update_Tree(1, pos[x], pos[y], c, id);
}
ull query(int x, int y) {
ull ans = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x,y);
ans += query_Tree(1, pos[top[x]], pos[x]);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x,y);
ans += query_Tree(1, pos[x], pos[y]);
return ans;
}
int main(){
Val = -1;
while (~scanf("%d", &n)) {
Init();
for (int i = 2; i <= n; i++) {
int u;
scanf("%d", &u);
add(u, i); add(i, u);
}
dfs(1, 1, 1);
dfs(1, 1);
build_Tree(1, 1, n);
scanf("%d", &m);
while (m--) {
int opt, u, v;
ull x;
scanf("%d", &opt);
if (opt == 1) {
scanf("%d", &u);scanf("%d", &v);
scanf("%llu", &x);
calc(u, v, x, 0);
} else if (opt == 2) {
scanf("%d", &u);scanf("%d", &v);
scanf("%llu", &x);
calc(u, v, x, 1);
} else if (opt == 3) {
scanf("%d", &u);scanf("%d", &v);
calc(u, v, -1, 0);
calc(u, v, Val, 1);
} else {
scanf("%d", &u);scanf("%d", &v);
printf("%llu\n", query(u, v));
}
}
}
return 0;
}