Problem
给一个(n)个点,(m)条边的DAG,找到一个点,使得删去这个点后的最长路径最短。
(2 le n le 500000,1 le m le 1000000)。
Solution
看到DAG就想到拓扑。
求出以(x)为起点的最长路径长度(ds_x)和以(x)为终点的最长路径长度(dt_x)。可以通过跑正反图拓扑实现。
设第(x)个点的拓扑序为(TX_x)。
对于一条边(u o v),经过它的最长路径长度为(dt_u + ds_v + 1),正确性显然。
考虑枚举删的点,假设我们现在删除(x),我们假设所有(TX_i < TX_x)的点放在集合(A),(TX_i > TX_x)的放在集合(B),(x)在中间。
此时的最长路径分三种:(A o A,A o B,B o B)。
- (A o A):(max_{i in A} {dt_i})
- (A o B):(max_{(u,v) in E,u in A,v in B} {dt_u +ds_v + 1 })
- (B o B):(max_{i in B} {ds_i})
(x)原来在(B)集合,现在不在(B)集合,则删除(ds_x)。
(x)在反图中连接的点都存在第二种情况,删除。
下一次删除前,(x)在(A)集合,则加入(dt_x)。
(x)在图中连接的点都存在第二种情况,加入。
发现需要维护一个支持删除加入,求最值的东西,那么可以使用平衡树权值线段树。(其实multiset应该可以,但是我不会用)
# include<bits/stdc++.h>
using namespace std;
const int N = 500005,M = 1000005;
int n,m;
vector <int> g[N],g2[N];
int ds[N],dt[N];
int du[N],du2[N];
int Tx[N],Txtot = 0;
int q[N];
void topsort(void) //dt
{
queue <int> q;
for(int i = 1; i <= n; i++)
{
if(du[i] == 0)
{
q.push(i);
Tx[i] = ++Txtot;
}
}
while(!q.empty())
{
int x = q.front(); q.pop();
for(int i = 0; i < (int)g[x].size(); i++)
{
int v = g[x][i];
dt[v] = max(dt[v],dt[x] + 1);
if(--du[v] == 0) q.push(v),Tx[v] = ++Txtot;
}
}
return;
}
void topsort2(void)
{
queue <int> q;
for(int i = 1; i <= n; i++)
{
if(du2[i] == 0)
{
q.push(i);
}
}
while(!q.empty())
{
int x = q.front(); q.pop();
for(int i = 0; i < g2[x].size(); i++)
{
int v = g2[x][i];
ds[v] = max(ds[v],ds[x] + 1);
if(--du2[v] == 0) q.push(v);
}
}
return;
}
bool compare(const int &x,const int &y)
{
return Tx[x] < Tx[y];
}
struct node
{
int val,l,r;
}T[N << 2];
int tot = 1;
void build(int root,int l,int r)
{
if(l == r) {T[root].val = 0,T[root].l = T[root].r = 0; return;}
int mid = (l + r) >> 1;
T[root].l = ++tot,T[root].r = ++tot;
build(T[root].l,l,mid),build(T[root].r,mid + 1,r);
return;
}
void update(int root,int l,int r,int x,int d)
{
if(l == r && l == x)
{
T[root].val += d;
return;
}
int mid = (l + r) >> 1;
if(l <= x && x <= mid) update(T[root].l,l,mid,x,d);
else if(x > mid) update(T[root].r,mid + 1,r,x,d);
T[root].val = T[T[root].l].val + T[T[root].r].val;
return;
}
int query(int root,int l,int r)
{
if(l == r) return l;
int mid = (l + r) >> 1;
if(T[T[root].r].val > 0) return query(T[root].r,mid + 1,r);
else return query(T[root].l,l,mid);
}
int main(void)
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= m; i++)
{
int a,b; scanf("%d%d",&a,&b);
g[a].push_back(b),g2[b].push_back(a);
++du[b],++du2[a];
}
topsort(),topsort2();
build(1,1,2 * n);
for(int i = 1; i <= n; i++)
{
update(1,1,2 * n,ds[i],1); q[i] = i;
}
sort(q + 1, q + n + 1, compare);
int ans = 1e9 + 7,id = 0;
for(int i = 1; i <= n; i++) // delete i
{
update(1,1,2 * n,ds[q[i]],-1);
for(int j = 0; j < (int)g2[q[i]].size(); j++)
{
int v = g2[q[i]][j];
update(1,1,2 * n,dt[v] + 1 + ds[q[i]],-1);
}
// printf("i = %d,max = %d
",i,Max);
int Max = query(1,1,2 * n);
// printf("i = %d,Max = %d
",i,Max);
if(Max < ans)
{
ans = Max,id = q[i];
}
// S.insert(dt[i]);
update(1,1,2 * n,dt[q[i]],1);
for(int j = 0; j < (int)g[q[i]].size(); j++)
{
int v = g[q[i]][j];
update(1,1,2 * n,dt[q[i]] + 1 + ds[v],1);
}
}
printf("%d %d
",id,ans);
return 0;
}