【bzoj3832】Rally
Portal -->bzoj3832
Description
? 給你一個DAG,每條邊長度都是\(1\),請找一個點滿足刪掉這個點之後剩余圖中的最長路最短
Solution
?? 這題的話感覺思路挺妙的
?? 因為要涉及到刪點操作,所以我們肯定不能通過直接的方法來算最長路,然後因為這題沒有固定的起點和終點並且是一個DAG,所以有一個比較簡單粗暴的處理方法就是像網絡流一樣建一個超級源\(S\)和一個超級匯\(T\),每個點都從\(S\)連一條長度為\(0\)的邊,每個點都向\(T\)連一條長度為\(0\)的邊
? 我們記\(f[i]\)表示從\(S\)到\(i\)點的最長路,記\(g[i]\)
?? 這個時候,考慮\(S\)到\(T\)的一個割(註意:這裏不一定是最小割!只是一個普通的割即可),那麽根據其定義,所有從\(S\)到\(T\)的路徑必定經過割集中的最少一條邊,也就是說,如果我們將\(f[u]+1+g[v]\)看成原圖中\((u,v)\)這條邊的邊權,將\((S,i)\)的邊權看成\(f[i]\),\((i,T)\)的邊權看成\(g[i]\)
?? 那麽現在我們只要知道刪掉一個點之後,剩余圖中\(S\)到\(T\)的一個割就可以知道刪掉這個點的答案了,至此我們已經完成了問題的轉化,現在的問題是我們怎麽維護這個割集
?? 我們考慮用一個數據結構來快速獲得邊權最大值(線段樹或者堆都ok)
?? 假設一開始我們先把所有的\((i,T)\)的邊全部斷掉(這顯然是一個可行的割),也就是先將所有的\(g[i]\)丟進數據結構裏面,現在考慮刪掉一個點\(x\)的影響:所有拓撲序在\(x\)之後的點的\(f\)值可能會被影響,所有拓撲序在\(x\)之前的點的\(g\)值可能會被影響,這個時候為了保證正確性我們應該將當前割集中可能會被改變邊權的邊全部刪掉
?? 但是這樣怎麽保證當前最大值一定在數據結構裏面呢?這裏需要一點關於操作順序的技巧,下面先將過程講一下:
? 我們考慮按照拓撲序從小到大依次計算刪除這個點之後的最長路,對於當前枚舉到的點\(x\),我們進行如下操作:
1、將\((x,T)\)這條邊刪掉,將所有的\((u,x)\)刪掉(如果本來就沒有就不進行操作)
2、統計當前割集中邊權的最大值,更新答案
3、將\((S,x)\)這條邊加入割集,將所有的\((x,v)\)加入割集
?? 具體一點的話就是,首先\(1\)操作就是將割集中所有與\(x\)有關的邊全部刪掉,\(2\)不用說,關鍵是\(3\)操作,為什麽我們還原割集的時候不是將\(1\)操作中刪除的邊加回進去呢?
?? 因為我們是按照拓撲序從小到大刪點的,根據我們前面分析出來的刪點影響,在刪除後面的點的時候,\(g[x]\)是可能會被影響的值(轉移過來的邊可能會被刪掉),因此為了保證正確性我們不能將其留在割集中,同時為了保證這是一個割集,相對應的邊權不會被影響到並且效果類似的\((S,x)\)就應該被加入割集,同樣的道理\((u,x)\)的邊權中的\(g[x]\)可能會被影響所以要刪掉,然後為了保證一定是一個割集所以要加入\((x,v)\)這類邊
?? 那麽會不會出現有的邊的貢獻作為答案沒有被統計的情況呢?這個。。是不會的因為如果說\((u,x)\)這類邊的貢獻是答案的話,一定會在前面被計算到
?? 然後這題就十分愉快地做完啦ovo時間復雜度\(O(nlogn)\)
?
?? 代碼大概長這個樣子
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=500010,M=1000010,SEG=N*4,inf=2147483647;
struct xxx{
int y,nxt;
}a[M*2];
queue<int> q;
int h1[N],h[N],in[N],out[N],f[N],g[N];
int rec[N];
int n,m,tot,ans,ansnode;
namespace Seg{/*{{{*/
int ch[SEG][2],mx[SEG],cnt[SEG];
int n,tot;
void pushup(int x){mx[x]=max(mx[ch[x][0]],mx[ch[x][1]]);}
void _build(int x,int l,int r){
mx[x]=-1;
if (l==r) return;
int mid=l+r>>1;
ch[x][0]=++tot; _build(ch[x][0],l,mid);
ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
}
void build(int _n){tot=1; n=_n; _build(1,1,n);}
void _insert(int x,int d,int lx,int rx,int delta){
if (lx==rx){
cnt[x]+=delta;
if (cnt[x]>0) mx[x]=lx;
else cnt[x]=0,mx[x]=-1;
return;
}
int mid=lx+rx>>1;
if (d<=mid) _insert(ch[x][0],d,lx,mid,delta);
else _insert(ch[x][1],d,mid+1,rx,delta);
pushup(x);
}
void insert(int d,int delta){_insert(1,d,1,n,delta);}
int query(){return mx[1];}
}/*}}}*/
void add(int x,int y){
a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;
a[++tot].y=x; a[tot].nxt=h1[y]; h1[y]=tot;
}
void prework(){
int u,v;
while (!q.empty()) q.pop();
for (int i=1;i<=n;++i)
if (!in[i]) q.push(i),f[i]=0;
rec[0]=0;
while (!q.empty()){
v=q.front(); q.pop(); rec[++rec[0]]=v;
for (int i=h[v];i!=-1;i=a[i].nxt){
u=a[i].y;
f[u]=max(f[u],f[v]+1);
--in[u];
if (!in[u]) q.push(u);
}
}
for (int i=1;i<=n;++i)
if (!out[i]) q.push(i),g[i]=0;
while (!q.empty()){
v=q.front(); q.pop();
for (int i=h1[v];i!=-1;i=a[i].nxt){
u=a[i].y;
g[u]=max(g[u],g[v]+1);
--out[u];
if (!out[u]) q.push(u);
}
}
}
void solve(){
int x,u,tmp;
ans=inf; ansnode=0;
Seg::build(n+1);
for (int i=1;i<=n;++i) Seg::insert(g[i]+1,1);
for (int i=1;i<=n;++i){
x=rec[i];
Seg::insert(g[x]+1,-1);
for (int j=h1[x];j!=-1;j=a[j].nxt){
u=a[j].y;
Seg::insert(f[u]+1+g[x]+1,-1);
}
tmp=Seg::query();
if (ans>tmp)
ans=tmp,ansnode=x;
Seg::insert(f[x]+1,1);
for (int j=h[x];j!=-1;j=a[j].nxt){
u=a[j].y;
Seg::insert(f[x]+1+g[u]+1,1);
}
}
printf("%d %d\n",ansnode,ans-1);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y;
scanf("%d%d",&n,&m);
memset(h,-1,sizeof(h));
memset(h1,-1,sizeof(h1));
tot=0;
for (int i=1;i<=m;++i){
scanf("%d%d",&x,&y);
add(x,y);
++in[y]; ++out[x];
}
prework();
solve();
}
【bzoj3832】Rally