CF1076E Vasya and a Tree
阿新 • • 發佈:2018-11-16
題意
思考
簡要題意就是給定多個操作,每次操作將與 \(u\) 距離小於等於 \(k\) 且在 \(u\) 子樹內的點點權值加 \(x\) ,輸出最終各個點的權值。
看這題的時候我先想的是樹剖,發現樹剖並不好處理兩點的距離限制
第二種想法是 \(bfs\) 序,想想可能不好處理(不考慮超時的話可以做,線段樹維護 \(bfs\) 序,對於每一層我們知道節點編號的區間,然後每一層二分割槽間左右端點來判斷是否在 \(u\) 的子樹內,再更新,時間複雜度有、爆炸)
於是當時這題成功地沒有做出來,我過於菜
考完看了看別人的程式碼...發現自己智障了...
考慮單獨的一個節點 \(u\)
- 進入該點
- 實現該點的所有操作
- 遞迴它的子樹
- 撤銷操作
這樣 \(u\) 的操作就不會影響到其他子樹上的點了,還有一個好處,我們並不需要修改深度 \(dep(u)\)~\(dep(u)+k\) 而只需修改 \(dep(k)\) 最後查詢的時候查 \(sum(dep(u)\)~\(sum(max))\)
程式碼
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll read(){ ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x * f; } const ll N = 300030; struct node{ ll nxt, to; }edge[N << 1]; ll head[N], num; void build(ll from, ll to){ edge[++num].nxt = head[from]; edge[num].to = to; head[from] = num; } ll c[N], n, m; ll lowbit(ll x){ return x & -x; } void add(ll pos, ll v){ for(ll i=pos; i<=n; i+=lowbit(i)) c[i] += v; } ll query(ll pos){ ll ans = 0; for(ll i=pos; i; i-=lowbit(i)) ans += c[i]; return ans; } vector<ll> val[N], D[N]; ll ans[N]; void dfs(ll u, ll f, ll d){ for(ll i=0; i<D[u].size(); i++){ add(min(n, D[u][i] + d), val[u][i]); } ans[u] = query(n) - query(d - 1); for(ll i=head[u]; i; i=edge[i].nxt){ ll v = edge[i].to; if(v == f) continue; dfs(v, u, d + 1); } for(ll i=0; i<D[u].size(); i++){ add(min(n, D[u][i] + d), -val[u][i]); } } int main(){ n = read(); for(ll i=1; i<=n-1; i++){ ll u = read(), v = read(); build(u, v); build(v, u); } m = read(); for(ll i=1; i<=m; i++){ ll u = read(), d = read(), v = read(); D[u].push_back(d); val[u].push_back(v); } dfs(1, 0, 1); for(ll i=1; i<=n; i++){ cout << ans[i] << " "; } return 0; }
總結
注意題目性質,樹上的技巧要多學習……