1. 程式人生 > >CF1076E Vasya and a Tree

CF1076E Vasya and a Tree

題意

Here

思考

簡要題意就是給定多個操作,每次操作將\(u\) 距離小於等於 \(k\) 且在 \(u\) 子樹內的點點權值加 \(x\) ,輸出最終各個點的權值。

看這題的時候我先想的是樹剖,發現樹剖並不好處理兩點的距離限制

第二種想法是 \(bfs\) 序,想想可能不好處理(不考慮超時的話可以做,線段樹維護 \(bfs\) 序,對於每一層我們知道節點編號的區間,然後每一層二分割槽間左右端點來判斷是否在 \(u\) 的子樹內,再更新,時間複雜度有、爆炸)

於是當時這題成功地沒有做出來,我過於菜

考完看了看別人的程式碼...發現自己智障了...

考慮單獨的一個節點 \(u\)

,它的操作影響的都是在它子樹內的與 \(u\) 深度差小於等於 \(k\) 的節點,那麼我們只要維護當前深度操作總和就行了(樹狀陣列或線段樹),但有一個問題,由於 \(u\) 點的操作隻影響 \(u\) 的子樹,其他節點怎麼辦 ? 我們可以先將所有操作離線,再總體按 \(dfs\) 實現:

  1. 進入該點
  2. 實現該點的所有操作
  3. 遞迴它的子樹
  4. 撤銷操作

這樣 \(u\) 的操作就不會影響到其他子樹上的點了,還有一個好處,我們並不需要修改深度 \(dep(u)\)~\(dep(u)+k\) 而只需修改 \(dep(k)\) 最後查詢的時候查 \(sum(dep(u)\)~\(sum(max))\)

就好,因為當查詢到這個點時,只有 \(u\) 的祖先節點的操作實現了,那麼對於任意的 \(u\) 的兒子節點,如果它的值有修改,說明這個修改操作是來自 \(u\) 的祖先節點的,所以 \(u\) 肯定也會被修改

程式碼

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

總結

注意題目性質,樹上的技巧要多學習……