1. 程式人生 > >[bzoj3832]Rally——拓撲排序+堆

[bzoj3832]Rally——拓撲排序+堆

題目大意:

給定一個N個點M條邊的有向無環圖,每條邊長度都是1。 請找到一個點,使得刪掉這個點後剩餘的圖中的最長路徑最短。

思路:

首先我們加一個超級源S和一個超級匯T,然後整個題目就變成了求S>TS->T的最長鏈。計算出S到每一個點的最長路和每一個點到T的最長路,這樣我們就可以很方便地算出來經過任意一條邊的最長路了。 考慮到刪除一個點之後新的最長鏈一定會經過一些邊,於是我們可以維護一個集合代表刪除了這個點之後新的最長路可能會經過哪些邊。 假設我們刪除了點u。 首先我們所維護的集合內的邊一定和點u沒有偏序關係,否則經過這條邊的最長路可能會經過點u。 其次有偏序關係的邊也沒有必要重複放入集合中。 於是一個集合我們可以看成是一個割,其中的邊都是平行的,不難發現圖中的所有路徑都經過了這個割中的邊。 刪除一個點之後我們將這個割中和這個點相連的所有邊去掉,剩下的邊集的答案便一定是去掉了這個點之後的最長路的長度。 於是接下來就只需要動態維護這個割集並使它滿足上面的條件,按照拓撲序的過程,維護一個指向目前的點集的邊集,每次選定一個點將所有指向它的邊刪除,這個集合便一定滿足性質,計算完之後再將u刪除,將所有從u出發的邊加入集合即可。 上述的過程可以用權值線段樹或堆來實現。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
	freopen("bzoj3832.in","r",stdin);
	freopen
("bzoj3832.out","w",stdout); } template<typename T>void read(T &_){ T __=0,mul=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')mul=-1; ch=getchar(); } while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar(); _=__*mul; } const int maxn=5e5+10; const int maxm=2e6
+10; const int inf=0x3f3f3f3f; int n,m,f[maxn],g[maxn]; int beg[maxn],las[maxm],to[maxm],from[maxm],cnte=1; void add(int u,int v){ las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; from[cnte]=u; } namespace dp{ void get_f(){ int deg[maxn]={0}; queue<int>qu; REP(i,2,cnte)++deg[to[i]]; REP(i,0,n+1)if(!deg[i])qu.push(i),f[i]=0; while(!qu.empty()){ int u=qu.front(); qu.pop(); for(int i=beg[u];i;i=las[i]){ int v=to[i]; --deg[v]; f[v]=max(f[v],f[u])+1; if(!deg[v])qu.push(v); } } } void get_g(int u){ if(g[u]!=-1)return; for(int i=beg[u];i;i=las[i]){ int v=to[i]; get_g(v); g[u]=max(g[u],g[v]+1); } if(g[u]==-1)g[u]=0; } } void init(){ read(n); read(m); int u,v; REP(i,1,m)read(u),read(v),add(u,v); REP(i,1,n)add(0,i),add(i,n+1); memset(f,-1,sizeof(f)); memset(g,-1,sizeof(g)); dp::get_f(); dp::get_g(0); } int val[maxm],deg[maxn],ans=inf,id; queue<int>qu; vector<int>pre[maxn]; priority_queue<pii>h; bool del[maxm]; void work(){ REP(i,2,cnte){ val[i]=f[from[i]]+g[to[i]]+1; ++deg[to[i]]; } qu.push(0); while(!qu.empty()){ int u=qu.front(); qu.pop(); REP(i,0,pre[u].size()-1) del[pre[u][i]]=1; while(!h.empty() && del[h.top().se])h.pop(); if(!h.empty() && h.top().fi<ans)ans=h.top().fi,id=u; for(int i=beg[u];i;i=las[i]){ int v=to[i]; --deg[v]; h.push(mk(val[i],i)); pre[v].pb(i); if(!deg[v])qu.push(v); } } printf("%d %d\n",id,ans-2); } int main(){ File(); init(); work(); return 0; }