1. 程式人生 > >CodeForces - 276E Little Girl and Problem on Trees(線段樹)

CodeForces - 276E Little Girl and Problem on Trees(線段樹)

題意
現在給你一棵這樣的樹,除了根節點外,其他的所有節點度都為2,也就是說除了根節點之外其他的節點都只能有一個節點,現在有兩種操作:
1。以當前節點為中心,距離為d的範圍內的點全部都加上x。
2。查詢某個點的值
思路*

在這裡插入圖片描述
觀察這樣一張圖,你會發現他們其實都是一條一條的鏈狀的,所以對於更新來說我們可以這樣,我們建兩顆線段樹,第一顆線段樹中我們就是維護每個鏈上的值,每次更新的時候我們就以節點V為中心,向上或向下延申D的距離,這些區間我們加上x,那麼會有兩種情況,第一種就是我們向上延申的時候超過根節點我們怎麼辦?這樣我們就建第二顆線段樹,他維護的就是當我們向上延申超過根節點的時候我們直接線上段樹的層上打標記,那麼對於每次查詢我們就查這個點本身的權值已經他所在的層數的值,第二種就是向下更新超過了他的葉子節點我們怎麼辦, 我們記錄下來每個節點下面的兒子幾點的數量然後每次更新的時候取個min就好了具體上程式碼,,程式碼寫的很噁心。。。
程式碼

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 1e5 + 10;
vector<int>V[maxn];
int in[maxn] , de[maxn] , son[maxn];
int tot,n,q;
void dfs(int u, int fa,int dept)
{
    in[u] = ++tot; //DFS序 
    de[
u] = dept ; // 深度 son[u] = 0; // 當前節點的兒子節點的個數 for(int i = 0 ; i < V[u].size() ; i++) { int v = V[u][i]; if(v == fa) continue; dfs(v,u,dept+1); son[u] = son[v] + 1; } } struct seg { int tree[maxn<<2]; int add[maxn<<2]; void pushup
(int rt) { tree[rt] = tree[rt<<1] + tree[rt<<1|1]; } void pushdown(int L,int R, int rt) { if(add[rt] == 0) return ; int m = (L+R)>>1; add[rt<<1] += add[rt]; add[rt<<1|1] += add[rt]; tree[rt<<1] += add[rt] * (m-L+1); tree[rt<<1|1] += add[rt] * (R-m); add[rt] = 0; } void build(int l,int r,int rt) { tree[rt] = add[rt] = 0; if(l == r) return ; int m = (r+l)>>1; build(lson); build(rson); } void update(int L,int R,int val,int l,int r,int rt) { if(L <= l && R >= r) { tree[rt] += val * (r-l+1); add[rt] += val; return ; } pushdown(l,r,rt); int m = (r+l)>>1; if(L <= m) update(L,R,val,lson); if(R > m) update(L,R,val,rson); pushup(rt); } int query(int pos,int l,int r,int rt) { if(l == r) return tree[rt]; pushdown(l,r,rt); int m = (r+l)>>1; if(pos <= m) return query(pos,lson); else return query(pos,rson); } void print(int l,int r,int rt) { if(l == r) return ; pushdown(l,r,rt); int m = (l+r)>>1; print(lson); print(rson); } }Leve,List; void update(int v,int x,int d) { if(v == 1) // 如果我們直接更新的是1號節點,那麼其實我們直接對於層數打標記就好了,注意一下根據樣例可以看出他是d+1 不是d { Leve.update(1,d+1,x,1,n,1); return ; } if(de[v] >= d) // 如果當前深度大於我們d,就說明我們不需要對層打標記,直接在鏈上打標記就行 { int L = in[v] - d; // 那麼我們更新的區間就是他們DFS序-d int R = (son[v] < d) ? (in[v] + son[v]) : (in[v] + d); // R就是 看看他向下可以更新的取min if(de[v] == d) { List.update(1,1,x,1,n,1); List.update(L+1,R,x,1,n,1); } else List.update(L,R,x,1,n,1); } else { int len = d - de[v]; // 我們先找到需要向上更新的層數 Leve.update(1,len+1,x,1,n,1); // 把層數更新一下 int L = in[v] - (de[v] - len) + 1; // 那麼我們在鏈上的更新其實就是他的層數更新的下一個節點 int R = (son[v] < d) ? (in[v] + son[v]) : (in[v] + d); List.update(L,R,x,1,n,1); } } int query(int v) { return Leve.query(de[v]+1,1,n,1) + List.query(in[v],1,n,1); } signed main() { tot = 0; scanf("%lld%lld",&n,&q); Leve.build(1,n,1); List.build(1,n,1); for(int i = 1 ; i <= n ; i++) V[i].clear(); for(int i = 0 ; i < n - 1 ; i++) { int a,b;scanf("%lld%lld",&a,&b); V[a].push_back(b); V[b].push_back(a); } dfs(1,-1 , 0); int op; while(q--) { scanf("%lld",&op); if(op == 0) { int v,x,d;scanf("%lld%lld%lld",&v,&x,&d); update(v,x,d); } else if(op == 1) { int v;scanf("%lld",&v); printf("%lld\n",query(v)); } } }