给出一个n个点m条边的DAG,求删除哪一个点及其所有边使剩余图中的最长路径最短,并求出最短路径。
n<=75000,m<=100000
拓扑排序+线段树
一张图可能存在连通块,于是对于每个点增设类似网络流的源汇点S,T,问题转化为删点后S到T的最长路。
对于每个点i计算点S到点i的最长距离f[i],点i到点T的最长距离g[i],对于一条边(u,v),它所在最长路即为f[u]+1+g[v],记为该边的路径长。
用权值线段树维护删除每个点之后剩余图的最长路径。首先将每个点的g[i]插入线段树。接着按照拓扑序删除点。删除点i时,在线段树中删除点i所有入边的路径长,以及g[i]。此时剩余图的最长路径即线段树中的最大值。继续删除点i+1之前,将f[i],以及i的所有出边的路径长加入线段树。
此思路的关键在于拓扑序。之所以能够保证删除点i后线段树中不存在与点i相关的路径,是对于因为拓扑序小于i的点j,若其与i直接相连,则计算前被删除,若间接相连,则j可能包含点i的最长路在删除j时已经被删除;对于拓扑序大于i的点j,线段树中仅保存了g[j],不经过i。
#include
#include
#include
const int sm = 75000 + 53;
const int sn = 25e4 + 53;
const int Inf = 0x3f3f3f3f;
int n,m,S,T,tot,cnt,lim;
int deg[sm],_deg[sm];
bool ex[sm];
int to[sn],nxt[sn],hd[sm],c[sn];
int _to[sn],_nxt[sn],_hd[sm],_c[sn];
int f[sm],g[sm];
int Min(int x,int y) { return xint Max(int x,int y) { return x>y?x:y; }
void Add(int u,int v,int w) {
to[++tot] = v, nxt[tot] = hd[u], hd[u] = tot;
c[tot] = w, deg[v]++;
}
void Ins(int u,int v,int w) {
_to[++cnt] = v, _nxt[cnt] = _hd[u], _hd[u] = cnt;
_c[cnt] = w, _deg[v]++;
}
int top,Stk[sm];
void Topb() {
for(int i = 1; i <= T; ++i) g[i] = -Inf;
Stk[++top] = T, g[T] = 0;
int t, j;
while(top) {
t = Stk[top--];
for(int i = _hd[t]; i; i = _nxt[i]) {
j = _to[i], --_deg[j];
g[j] = Max(g[j],g[t]+_c[i]);
if(_deg[j] == 0)
Stk[++top] = j;
}
}
}
int sort[sm];
void Topa() {
for(int i = 1; i <= T; ++i) f[i] = -Inf;
Stk[++top] = S, f[S] = 0;
int t;
while(top) {
sort[++sort[0]] = t = Stk[top--];
for(int i = hd[t]; i; i = nxt[i]) {
--deg[to[i]];
f[to[i]] = Max(f[to[i]],f[t] + c[i]);
if(deg[to[i]] == 0)
Stk[++top] = to[i];
}
}
}
namespace ST{
int sum[sm<<2];
void Insert(int rt,int l,int r,int k) {
++sum[rt];
if(l==r) return;
int m = (l + r) >> 1;
if(k <= m) Insert(rt<<1,l,m,k);
else Insert(rt<<1|1,m+1,r,k);
}
void Delete(int rt,int l,int r,int k) {
--sum[rt];
if(l==r) return;
int m = (l + r) >> 1;
if(k <= m) Delete(rt<<1,l,m,k);
else Delete(rt<<1|1,m+1,r,k);
}
int Query(int rt,int l,int r) {
if(l == r) return l;
int m = (l + r) >> 1;
if(sum[rt<<1|1] > 0) return Query(rt<<1|1,m+1,r);
return Query(rt<<1,l,m);
}
}
int main() {
freopen("chronosphere.in","r",stdin);
freopen("chronosphere.out","w",stdout);
int u,v;
scanf("%d%d",&n,&m);
S = n + 1, T = S + 1, lim = n + 5;
for(int i = 1; i <= m; ++i) {
scanf("%d%d",&u,&v);
Add(u,v,1), Ins(v,u,1);
ex[u] = ex[v] = 1;
}
for(int i = 1; i <= n; ++i)
if(ex[i]) {
Add(S,i,0), Add(i,T,0);
Ins(T,i,0), Ins(i,S,0);
}
Topa(), Topb();
for(int i = 1; i <= T; ++i)
ST::Insert(1,1,lim,g[i]);
int ans = Inf,k,q;
for(int i = 1; i <= sort[0]; ++i) {
k = sort[i];
for(int j = _hd[k]; j; j = _nxt[j])
ST::Delete(1,1,lim,(_c[j]+g[k]+f[_to[j]]));
ST::Delete(1,1,lim,g[k]);
q = ST::Query(1,1,lim);
if(q < ans) u = k, ans = q;
else if(q == ans) u = Min(u,k);
ST::Insert(1,1,lim,f[k]);
for(int j = hd[k]; j; j = nxt[j])
ST::Insert(1,1,lim,(c[j]+f[k]+g[to[j]]));
}
printf("%d %d\n",u,ans);
return 0;
}