- 一张(n)个点的(DAG),你可以删去一个点,要求最小化剩余图中的最长路径。
- (nle5 imes10^5,mle10^6)
过去点集与未来点集
一个非常奇妙的做法。
首先我们跑一遍拓扑,预处理出到每个点为止的最长路(d1_x)和从每个点开始的最长路(d2_x),这只要简单地(DAG)上(DP)一下就结束了。
然后按照拓扑序,考虑每一个点的答案。
有一个很神奇的思路,就是我们维护一个过去点集(A)和一个未来点集(B)(也就是操作过的点集和尚未操作的点集),那么最长路径只有三种可能:
- (A)内部:(max_{xin A}d1_x)。
- (B)内部:(max_{xin B}d2_x)。
- (A,B)之间:(max_{uin A,vin B,(u,v)in E}d1_u+1+d2_v)。
直接拿一个堆((multiset))维护。
然后现在我们要考虑一个点(k)的答案,由于是按拓扑序枚举的,它的前驱节点肯定都已经去了(A)中,它的后继节点肯定都还留在(B)中。
所以要删除它的贡献,对于在(B)内部的贡献只要删除(d2_k),对于(A,B)之间的贡献也只要删去所有由它产生的贡献(可以在计算贡献的时候就记录下来)。
那么此时的答案就是删除(k)时的答案了,与原有答案比较更新。
接下来我们要把(k)加入过去点集,对于在(A)内部的贡献只要加入(d1_k),对于(A,B)之间的贡献只要枚举它的所有出边计算(显然后继节点(to)都在(B)中,同时我们把这个贡献扔到(to)的桶中方便拿出(to)时贡献的删除)。
代码:(O(mlogn))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
#define M 1000000
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,m,deg[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[M+5];
vector<int> p[N+5];multiset<int> S;
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
char oc,FI[FS],*FA=FI,*FB=FI;
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
int q[N+5],d1[N+5],d2[N+5];I void Topo()//拓扑排序+预处理
{
RI i,j,H=1,T=0;for(i=1;i<=n;++i) !deg[i]&&(q[++T]=i);
W(H<=T) for(i=lnk[q[H++]];i;i=e[i].nxt) !--deg[e[i].to]&&(q[++T]=e[i].to);
for(i=1;i<=n;++i) for(j=lnk[q[i]];j;j=e[j].nxt) d1[e[j].to]=max(d1[e[j].to],d1[q[i]]+1);//预处理到每个点为止的最长路
for(i=n;i>=1;--i) for(j=lnk[q[i]];j;j=e[j].nxt) d2[q[i]]=max(d2[q[i]],d2[e[j].to]+1);//预处理从每个点出发的最长路
}
int main()
{
RI i,j,x,y;for(read(n,m),i=1;i<=m;++i) read(x,y),add(x,y),++deg[y];Topo();
#define T(v) (S.insert(v))
#define E(v) (S.erase(S.find(v)))
RI t,d=1e9,nd;for(i=1;i<=n;++i) T(d2[i]);for(i=1;i<=n;++i)
{
E(d2[q[i]]);for(vector<int>::iterator it=p[q[i]].begin();it!=p[q[i]].end();++it) E(*it);//从未来点集中删除
(nd=S.empty()?0:*--S.end())<d&&(t=q[i],d=nd);//计算当前答案
T(d1[q[i]]);for(j=lnk[q[i]];j;j=e[j].nxt) p[e[j].to].emplace_back(nd=d1[q[i]]+1+d2[e[j].to]),T(nd);//加入到过去点集
}return printf("%d %d
",t,d),0;
}