Codeforces 396C (DFS序+線段樹)

題面

傳送門
題目大意:
給定一棵樹,每個點都有權值,邊的長度均爲1,有兩種操作
操作1:將節點u的值增加x,並且對於u的子樹中的任意一個點v,將它的值增加x-dist(u,v)*k, dist(u,v)表示u,v之間的距離
操作2:查詢節點u的值

分析

這類題目需要用到一個重要的思想:將樹上操作轉化爲區間操作
通過DFS序我們可以實現這一點.
對於每個節點x,我們記錄它在前序遍歷中的位置l[x],再一次回到x時的序號r[x],則x及其子樹的區間爲前序遍歷中的[l[x],r[x]]
如:

這棵樹的前序遍歷爲0 1 4 5 2 6 3 7 8 9 10
後序遍歷爲4 5 1 6 2 7 8 9 10 3 0
對於點3來說,它在前序遍歷中的序號爲7,遍歷完7,8,9後回到3的序號爲10,則區間爲[7,10]
將樹上操作轉化爲區間之後,我們處理兩種操作
顯然是用線段樹的區間修改和單點查詢實現
每次修改時,對於u的後代v,我們發現它增加的值=x+k(d[v]d[u])=x+k×d[u]k×d[v] (d[x]表示x的深度)
其中,對於u的每個後代v,x+k×d[u] 都是一樣的,可以批量修改,而k×d[v] 則由每個節點決定
因此,我們用兩棵線段樹維護
一棵維護x+k×d[u] ,一棵維護k
修改時,我們把x+k×d[u]k 分別累加到區間[l[u],r[u]]中每一個點
查詢時,我們可以求出每個節點的x+k×d[u] 的總和a,以及k 的總和b
答案就是abd[u]

時間複雜度O(n+qlog2n)

代碼

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 300005
#define mod 1000000007ll
using namespace std;
int n,q; 
struct edge{
    int from;
    int to;
    int next;
}E[maxn<<1];
int size;
int head[maxn];
void add_edge(int u,int v){
    size++;
    E[size].from=u;
    E[size].to=v;
    E[size].next=head[u];
    head[u]=size; 
}

struct segment_tree{
    struct node{
        int l;
        int r;
        long long mark;
        long long v;
    }tree[maxn<<2];
    segment_tree(){
        memset(tree,0,sizeof(tree));
    }
    void build(int l,int r,int pos){
        tree[pos].l=l;
        tree[pos].r=r;
        tree[pos].mark=0;
        tree[pos].v=0;
        if(l==r) return;
        int mid=(l+r)>>1;
        build(l,mid,pos<<1);
        build(mid+1,r,pos<<1|1);
    }
    void push_down(int pos){
        if(tree[pos].mark){
            tree[pos<<1].mark=(tree[pos].mark+tree[pos<<1].mark)%mod;
            tree[pos<<1|1].mark=(tree[pos].mark+tree[pos<<1|1].mark)%mod;
            tree[pos<<1].v=(tree[pos].mark+tree[pos<<1].v)%mod;
            tree[pos<<1|1].v=(tree[pos].mark+tree[pos<<1|1].v)%mod;
            tree[pos].mark=0;
        }
    }
    void update(int L,int R,long long v,int pos){
        if(L<=tree[pos].l&&R>=tree[pos].r){
            tree[pos].v=(tree[pos].v+v)%mod;
            tree[pos].mark=(tree[pos].mark+v)%mod;
            return ;
        }
        push_down(pos);
        int mid=(tree[pos].l+tree[pos].r)>>1;
        if(L<=mid) update(L,R,v,pos<<1);
        if(R>mid) update(L,R,v,pos<<1|1);
        return;
    } 
    long long query(int L,int R,int pos){
        if(L<=tree[pos].l&&R>=tree[pos].r){
            return tree[pos].v;
        }
        push_down(pos);
        int mid=(tree[pos].l+tree[pos].r)>>1;
        long long ans=0;
        if(L<=mid) ans=(ans+query(L,R,pos<<1))%mod;
        if(R>mid) ans=(ans+query(L,R,pos<<1|1))%mod;
        return ans;
    }
}; 
segment_tree T1,T2; 
int l[maxn],r[maxn];
int deep[maxn];
int cnt=0;
void dfs(int x,int fa){
    l[x]=++cnt;
    deep[x]=deep[fa]+1;
    for(int i=head[x];i;i=E[i].next){
        int y=E[i].to;
        if(y!=fa){
            dfs(y,x);
        }
    }
    r[x]=cnt;
} 
int main(){
    int p,cmd;
    int v,x,k;
    scanf("%d",&n);
    int root;
    for(int i=2;i<=n;i++){
        scanf("%d",&p);
            add_edge(i,p);
            add_edge(p,i);
    }
    dfs(1,0);
    T1.build(1,n,1);
    T2.build(1,n,1);
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        scanf("%d",&cmd);
        if(cmd==1){
            scanf("%d %d %d",&v,&x,&k);
            T1.update(l[v],r[v],(long long)x+(long long)deep[v]*k%mod,1);
            T2.update(l[v],r[v],(long long)k,1);
        }else{
            scanf("%d",&v);
            long long ans=(T1.query(l[v],l[v],1)-(long long)deep[v]*T2.query(l[v],l[v],1)+mod)%mod;
            if(ans<0) ans+=mod;
            printf("%I64d\n",ans%mod);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章