1. 程式人生 > >Codeforces 1110F(DFS序+線段樹)

Codeforces 1110F(DFS序+線段樹)

_id problem 在線 tdi cti ref eas UNC 發現

題面

傳送門

分析

next_id = 1
id = array of length n filled with -1
visited = array of length n filled with false

function dfs(v):
    visited[v] = true
    id[v] = next_id
    next_id += 1
    for to in neighbors of v in increasing order:
        if not visited[to]:
            dfs(to)

觀察題目中的這段偽代碼,發現實際上就是求出每個節點的DFS序,

註意for to in neighbors of v in increasing order:,要按編號從小到大訪問每個節點,所以要對鄰接表排序(可以用vector實現)

對詢問離線,每個結點保存由該節點出發所有詢問

第一次DFS,

求出每個點到根節點的距離,以及DFS序。順便把每個節點的子樹對應的DFS序範圍求出,記為l[x],r[x]

用一棵線段樹存儲距離,第i個節點存儲DFS序為i的樹結點到當前詢問節點的距離(初始詢問節點為1)(註意到求的是到葉子節點的最近距離,所以把非葉子節點的值設為INF

第二次DFS,

當DFS到節點x時,線段樹中存儲的距離恰好是詢問節點x到各節點的距離,

對於每個詢問,直接在線段樹上查詢區間最小值即可

對從x到兒子y,我們需要更新線段樹的值,將x到各節點的距離改成y到各節點的距離

設x到y的距離為len,發現對於y的子樹中的節點,距離會減少len,而對於其他節點,距離會增加len

由於DFS序的性質,y子樹中的節點的DFS序是連續的一段,所以我們只要在線段樹上進行區間更新即可

更新完之後繼續DFS y節點,回溯時記得把線段樹恢復

代碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define maxn 500005
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
vector<pair<int,int> >E[maxn];
int n,t;
int cnt=0;
int l[maxn],r[maxn];
long long dis[maxn];
void dfs1(int x,int fa){
    l[x]=++cnt;
    for(int i=0;i<E[x].size();i++){
        int y=E[x][i].first;
        if(y!=fa){
            dis[y]=dis[x]+E[x][i].second;
            dfs1(y,x);
        }
    }
    r[x]=cnt;
}

struct node{
    int l;
    int r;
    long long mark;
    long long v;
}tree[maxn<<2];
void push_up(int pos){
    tree[pos].v=min(tree[pos<<1].v,tree[pos<<1|1].v);
}
void build(int l,int r,int pos){
    tree[pos].l=l;
    tree[pos].r=r;
    if(l==r){
        tree[pos].mark=tree[pos].v=0;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,pos<<1);
    build(mid+1,r,pos<<1|1);
    push_up(pos);
}
void push_down(int pos){
    if(tree[pos].mark){
        tree[pos<<1].mark+=tree[pos].mark;
        tree[pos<<1|1].mark+=tree[pos].mark;
        tree[pos<<1].v+=tree[pos].mark;
        tree[pos<<1|1].v+=tree[pos].mark;
        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+=v;
        tree[pos].mark+=v;
        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);
    push_up(pos);
}
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=INF;
    if(L<=mid)  ans=min(ans,query(L,R,pos<<1));
    if(R>mid) ans=min(ans,query(L,R,pos<<1|1));
    return ans;
}

struct range{
    int l;
    int r;
    long long ans;
    int id;
    range(){
        
    }
    range(int L,int R,int i){
        l=L;
        r=R;
        id=i;
        ans=0;
    }
    void debug(){
        printf("[%d,%d]",l,r);
    }
};
vector<range>q[maxn];

void dfs2(int x,int fa){
    for(int i=0;i<q[x].size();i++){//處理詢問
        q[x][i].ans=query(q[x][i].l,q[x][i].r,1);
    }
    for(int i=0;i<E[x].size();i++){
        int y=E[x][i].first;
        int len=E[x][i].second;
        if(y!=fa){
            update(l[y],r[y],-len,1);//更新距離
            if(l[y]>1) update(1,l[y]-1,len,1);//註意邊界條件
            if(r[y]<n) update(r[y]+1,n,len,1); 
            dfs2(y,x); 
            update(l[y],r[y],len,1);//記得把線段樹恢復成原狀
            if(l[y]>1) update(1,l[y]-1,-len,1);
            if(r[y]<n) update(r[y]+1,n,-len,1); 
        }
    }
}

long long ans[maxn];
int main(){
    int u,v,w,x,ll,rr;
    scanf("%d %d",&n,&t);
    for(int i=2;i<=n;i++){
        scanf("%d %d",&v,&w);
        E[v].push_back(make_pair(i,w));
        E[i].push_back(make_pair(v,w));
    }
    for(int i=1;i<=n;i++){
        sort(E[i].begin(),E[i].end());
    } 
    for(int i=1;i<=t;i++){
        scanf("%d %d %d",&x,&ll,&rr);
        q[x].push_back(range(ll,rr,i)); 
    }
    dfs1(1,0);
    build(1,n,1);
    for(int i=1;i<=n;i++){
        if(l[i]==r[i]) update(l[i],l[i],dis[i],1);//如果不是葉子節點,距離要設為INF
        else update(l[i],l[i],INF,1);
    }
    dfs2(1,0);
    for(int i=1;i<=n;i++){
        for(int j=0;j<q[i].size();j++){//按照輸入順序輸出
            ans[q[i][j].id]=q[i][j].ans;
        }
    }
    for(int i=1;i<=t;i++){
        printf("%I64d\n",ans[i]);
    }
}

Codeforces 1110F(DFS序+線段樹)