1. 程式人生 > >BZOJ4699 樹上的最短路(最短路徑+dfs序+線段樹+堆+並查集)

BZOJ4699 樹上的最短路(最短路徑+dfs序+線段樹+堆+並查集)

i++ names 特殊 lld pop lca code span 枚舉

  首先一般化的將下水道和塌陷看成一個東西。註意到在從源點出發的所有需要使用某條下水道的最短路徑中,該下水道只會被使用一次,該下水道第一個被訪問的點相同,且只會在第一個訪問的點使用該下水道。這個第一個訪問的點顯然就是正常dij過程中,該下水道第一個被取出的點。

  於是在dij過程中,取出了某個點進行更新後,就可以將所有起點包含它的邊刪掉了。但這樣仍然沒有復雜度保證,因為一條下水道會帶來很多需要更新的點,且每個點會被重復更新多次。

  那麽可以想到稍微魔改一下dij,將點和下水道均入堆,其中下水道的權值是其第一個被訪問的點的最短路+邊權。如果某次從堆中取出的是下水道,使用其更新所有還未被取出的點,這樣這條下水道已經發揮了所有功能可以被刪掉,而所有其更新的點的最短路一定已被其確定,不需要再次更新,可以打上標記;如果從堆中取出的是點,使用其更新所有還未被使用的下水道即可,同理可以將該點刪掉且下水道權值不會被再次更新。

  最後的問題是如何實現這一過程。冷靜一下,我們需要的操作只是找到一條下水道所連的所有未被標記的點、一個點所連的所有未被標記的下水道。前一個問題比較簡單,可以在樹上弄個並查集,維護其到根的路徑上的第一個未被標記的點,查詢時求個lca即可。後一個問題可以考慮枚舉該點每棵子樹,查詢是否存在一端在該子樹內一端在該子樹外的路徑,這可以用線段樹維護一下dfs序區間的路徑端點dfs序min和max,葉子處維護堆即可。註意特殊處理一下一端恰為該節點的路徑。

  好像7k代碼刷新了我的記錄了啊?又醜又長還跑的慢。

#include<bits/stdc++.h>
using namespace std;
int read() { int x=0,f=1;char c=getchar(); while (c<0||c>9) {if (c==-) f=-1;c=getchar();} while (c>=0&&c<=9) x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 250010 #define M 600010 #define ll long long int n,m,S,fa[N]; ll d[N]; struct road{int
l1,r1,l2,r2,c; }a[M]; struct data { int x,op;ll d; bool operator <(const data&a) const { return d>a.d; } }; priority_queue<data> q; vector<int> id; namespace segment_tree { struct data1 { int x,i; bool operator <(const data1&a) const { return x<a.x||x==a.x&&i<a.i; } bool operator ==(const data1&a) const { return x==a.x&&i==a.i; } }; struct data2 { int x,i; bool operator <(const data2&a) const { return x>a.x||x==a.x&&i>a.i; } bool operator ==(const data2&a) const { return x==a.x&&i==a.i; } }; struct node { int L,R;data1 min;data2 max; priority_queue<data2> ins_min,del_min; priority_queue<data1> ins_max,del_max; void update() { while (!del_min.empty()&&ins_min.top()==del_min.top()) ins_min.pop(),del_min.pop(); while (!del_max.empty()&&ins_max.top()==del_max.top()) ins_max.pop(),del_max.pop(); min=(data1){ins_min.top().x,ins_min.top().i}; max=(data2){ins_max.top().x,ins_max.top().i}; } void ins(int x,int i){ins_min.push((data2){x,i}),ins_max.push((data1){x,i}),update();} void del(int x,int i){del_min.push((data2){x,i}),del_max.push((data1){x,i}),update();} }tree[N<<2]; void up(int k){tree[k].min=min(tree[k<<1].min,tree[k<<1|1].min),tree[k].max=min(tree[k<<1].max,tree[k<<1|1].max);} void build(int k,int l,int r) { tree[k].L=l,tree[k].R=r; tree[k].min=(data1){N,0},tree[k].max=(data2){0,0}; if (l==r) { tree[k].ins_min.push((data2){N,0}), tree[k].ins_max.push((data1){0,0}); return; } int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } data1 query_min(int k,int l,int r) { if (tree[k].L==l&&tree[k].R==r) return tree[k].min; int mid=tree[k].L+tree[k].R>>1; if (r<=mid) return query_min(k<<1,l,r); else if (l>mid) return query_min(k<<1|1,l,r); else return min(query_min(k<<1,l,mid),query_min(k<<1|1,mid+1,r)); } data2 query_max(int k,int l,int r) { if (tree[k].L==l&&tree[k].R==r) return tree[k].max; int mid=tree[k].L+tree[k].R>>1; if (r<=mid) return query_max(k<<1,l,r); else if (l>mid) return query_max(k<<1|1,l,r); else return min(query_max(k<<1,l,mid),query_max(k<<1|1,mid+1,r)); } void ins(int k,int p,int x,int i) { if (tree[k].L==tree[k].R) {tree[k].ins(x,i);return;} int mid=tree[k].L+tree[k].R>>1; if (p<=mid) ins(k<<1,p,x,i); else ins(k<<1|1,p,x,i); up(k); } void del(int k,int p,int x,int i) { if (tree[k].L==tree[k].R) {tree[k].del(x,i);return;} int mid=tree[k].L+tree[k].R>>1; if (p<=mid) del(k<<1,p,x,i); else del(k<<1|1,p,x,i); up(k); } } using segment_tree::query_min; using segment_tree::query_max; using segment_tree::ins; using segment_tree::del; namespace tree { int p[N],t,fa[N][19],deep[N],dfn[N],size[N],cnt; struct data{int to,nxt;}edge[N<<1]; void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;} void dfs(int k) { size[k]=1;dfn[k]=++cnt; for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=fa[k][0]) { fa[edge[i].to][0]=k; deep[edge[i].to]=deep[k]+1; dfs(edge[i].to); size[k]+=size[edge[i].to]; } } void build() { fa[1][0]=1;deep[1]=1;dfs(1); for (int j=1;j<19;j++) for (int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; } int lca(int x,int y) { if (deep[x]<deep[y]) swap(x,y); for (int j=18;~j;j--) if (deep[fa[x][j]]>=deep[y]) x=fa[x][j]; if (x==y) return x; for (int j=18;~j;j--) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j]; return fa[x][0]; } int getfa(int k){return k==1?0:fa[k][0];} bool in(int x,int l,int r){return dfn[x]<l||dfn[x]>r;} bool cross(int x,int l,int r){return in(a[x].l1,l,r)&&!in(a[x].r1,l,r)||in(a[x].r1,l,r)&&!in(a[x].l1,l,r);} void push(int l,int r) { for (;;) { int u=query_min(1,l,r).i; if (cross(u,l,r)) id.push_back(u),del(1,dfn[a[u].l1],dfn[a[u].r1],u),del(1,dfn[a[u].r1],dfn[a[u].l1],u); else break; } for (;;) { int u=query_max(1,l,r).i; if (cross(u,l,r)) id.push_back(u),del(1,dfn[a[u].l1],dfn[a[u].r1],u),del(1,dfn[a[u].r1],dfn[a[u].l1],u); else break; } } void get(int k) { id.clear(); for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=fa[k][0]) { int x=edge[i].to,l=dfn[x],r=dfn[x]+size[x]-1; push(l,r); } for (;;) { int u=query_min(1,dfn[k],dfn[k]).i; if (u) id.push_back(u),del(1,dfn[a[u].l1],dfn[a[u].r1],u),del(1,dfn[a[u].r1],dfn[a[u].l1],u); else break; } } } using tree::deep; using tree::getfa; using tree::dfn; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void dijkstra() { q.push((data){S,0,0}); memset(d,42,sizeof(d)); for (int i=1;i<=n;i++) fa[i]=i;fa[S]=find(getfa(S)); while (!q.empty()) { data x=q.top();q.pop(); if (x.op==0) { d[x.x]=x.d;tree::get(x.x); for (int i=0;i<id.size();i++) q.push((data){id[i],1,x.d+a[id[i]].c}); } else { int u=a[x.x].l2,v=a[x.x].r2,w=tree::lca(u,v); for (u=find(u);deep[u]>=deep[w];u=find(u)) q.push((data){u,0,x.d}),fa[u]=find(getfa(u)); for (v=find(v);deep[v]>=deep[w];v=find(v)) q.push((data){v,0,x.d}),fa[v]=find(getfa(v)); } } } int main() { #ifndef ONLINE_JUDGE freopen("bzoj4699.in","r",stdin); freopen("bzoj4699.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(),S=read(); for (int i=1;i<n;i++) { int x=read(),y=read(),z=read(); tree::addedge(x,y),tree::addedge(y,x); m++,a[m].l1=a[m].r1=x,a[m].l2=a[m].r2=y,a[m].c=z; m++,a[m].l1=a[m].r1=y,a[m].l2=a[m].r2=x,a[m].c=z; } for (int i=1;i<=m-(n-1)*2;i++) a[i].l2=read(),a[i].r2=read(),a[i].l1=read(),a[i].r1=read(),a[i].c=read(); tree::build();segment_tree::build(1,1,n); for (int i=1;i<=m;i++) ins(1,dfn[a[i].l1],dfn[a[i].r1],i), ins(1,dfn[a[i].r1],dfn[a[i].l1],i); dijkstra(); for (int i=1;i<=n;i++) printf(LL,d[i]); return 0; }

BZOJ4699 樹上的最短路(最短路徑+dfs序+線段樹+堆+並查集)