【洛谷P3038 [USACO11DEC]牧草種植】【樹鏈剖分】【裸】【邊權修改與查詢】
阿新 • • 發佈:2018-11-08
【連結】
https://www.luogu.org/problemnew/show/P3038
【題意】
給出一棵n個節點的樹,有m個操作,操作為將一條路徑上的邊權加一或詢問某條邊的權值。
【思路】
樹鏈剖分的裸題。
但是這個題是在邊上進行操作,我們考慮把邊上的操作轉移到點上首先想一下最簡單的鏈的情況。
對於區間[l,r][l,r]的操作會影響r−l+1r−l+1個點,但只會影響r−lr−l條邊,那麼我們可以把每條邊的邊權都放在與它相連的兩個點
深度較深的點上,所以我們每次修改的時候都對(l,r](l,r]進行修改,查詢的時候也如此,具體怎麼實現呢?
只需要在查詢/修改的時候把左區間+1即可
【程式碼】
#include<bits/stdc++.h> using namespace std; const int maxn = 1e6 + 6; const int mod = 1e9 + 7; vector<int>v[maxn]; int a[maxn]; int deep[maxn]; int fa[maxn]; int tot[maxn]; int top[maxn]; int son[maxn]; int idx[maxn]; int b[maxn]; int cnt = 0; int root = 1; struct node { int l, r, size, v, lazy; }t[maxn]; int dfs1(int now, int f, int dep) { deep[now] = dep; fa[now] = f; tot[now] = 1; int maxson = -1; for (int x : v[now]) { if (x == f)continue; tot[now] += dfs1(x, now, dep + 1); if (tot[x] > maxson) { maxson = tot[x]; son[now] = x; } } return tot[now]; } void dfs2(int now, int topf) { idx[now] = ++cnt; a[cnt] = b[now]; top[now] = topf; if (!son[now])return; dfs2(son[now], topf); for (int nxt : v[now]) { if (!idx[nxt]) { dfs2(nxt, nxt); } } } void update(int p) { t[p].v = (t[p << 1].v + t[p << 1 | 1].v + mod) % mod; } void pushdown(int p) { if (t[p].lazy) { t[p << 1].v = (t[p << 1].v + t[p].lazy*t[p << 1].size) % mod; t[p << 1 | 1].v = (t[p << 1 | 1].v + t[p].lazy*t[p << 1 | 1].size) % mod; t[p << 1].lazy = (t[p << 1].lazy + t[p].lazy) % mod; t[p << 1 | 1].lazy = (t[p << 1 | 1].lazy + t[p].lazy) % mod; t[p].lazy = 0; } } void build(int p, int l, int r) { t[p].l = l; t[p].r = r; t[p].size = r - l + 1; if (l == r) { t[p].v = a[l]; return; } int mid = (l + r) >> 1; build(p << 1, l, mid); build(p << 1 | 1, mid + 1, r); update(p); } void intervalAdd(int p, int l, int r, int val) { if (l <= t[p].l&&r >= t[p].r) { t[p].v += t[p].size*val; t[p].lazy += val; return; } pushdown(p); int mid = (t[p].l + t[p].r) >> 1; if (l <= mid)intervalAdd(p << 1, l, r, val); if (r > mid)intervalAdd(p << 1 | 1, l, r, val); update(p); } int intervalAsk(int p, int l, int r) { if (l <= t[p].l&&r >= t[p].r) { return t[p].v; } pushdown(p); int mid = (t[p].l + t[p].r) >> 1; int ans = 0; if (l <= mid)ans += intervalAsk(p << 1, l, r); if (r > mid)ans += intervalAsk(p << 1 | 1, l, r); return ans; } int treeSum(int x, int y) { int ans = 0; while (top[x] != top[y]) { if (deep[top[x]] < deep[top[y]]) swap(x, y); ans += intervalAsk(1, idx[top[x]], idx[x]); x = fa[top[x]]; } if (deep[x] > deep[y])swap(x, y); if(x!=y)ans += intervalAsk(1, idx[x]+1, idx[y]); return ans; } void treeAdd(int x, int y, int val) { while (top[x] != top[y]) { if (deep[top[x]] < deep[top[y]])swap(x, y); intervalAdd(1, idx[top[x]], idx[x], val); x = fa[top[x]]; } if (deep[x] > deep[y])swap(x, y); if (x==y)return; intervalAdd(1, idx[x]+1, idx[y], val); } int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i < n; i++) { int x, y; scanf("%d%d", &x, &y); v[x].push_back(y); v[y].push_back(x); } dfs1(root, 0, 1); dfs2(root, root); build(1, 1, n); while (m--) { char op[3]; int x, y; scanf("%s%d%d", op, &x, &y); if (op[0] == 'P') { treeAdd(x, y, 1); } else { printf("%d\n", treeSum(x,y)); } } }