1. 程式人生 > >ACM-ICPC 2018 焦作賽區網路預賽 E 樹鏈剖分

ACM-ICPC 2018 焦作賽區網路預賽 E 樹鏈剖分

題目連結

題意:給定一棵以1為根的節點,存在路徑加,路徑乘,路徑的節點權值取反,查詢路徑和等四個操作。

思路:

典型的樹鏈剖分題目。

對於取反,可以轉化成減法操作,比如:
00011取反為:11100
等價於 (11111 - 00011) = 11100
即將原值乘上(-1),再加上 2len1len為二進位制的位數

這樣就可以把取反操作轉化為加法和乘法操作。

對於路徑加和乘法的維護,可以使用兩個變數add, mul。
則 線段樹的節點值為: summul+add


更新為+c時,則add+c
更新為c時,則

mulcaddc

此題得解。

程式碼:

#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; }