Codeforces 1110F(DFS序+線段樹)
阿新 • • 發佈:2019-02-09
_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序+線段樹)