先建一个$S$和$T$,$forall 1le ile n$连边$(S,i)$和$(i,T)$,则最长路即为$S到T的最长路-2$
对于这张DAG,求出一个拓扑序,点$i$为第$i$个(特别的,$id_{S}=0$且$id_{T}=n+1$),根据拓扑序的性质,对于一条路径,其$id$必然单调递增
枚举删除的点$k$,再枚举$S$到$T$的最长路上$id$中$k$的前驱后继(由于$S$和$T$,因此必然存在,强制不等于$k$),记作$i$和$j$($i$到$j$要有边),则答案为$max(ed_{i}+st_{j}+1)-2$(分别表示从$i$到$S$和从$j$到$T$的最长路,可以预处理)
暴力枚举复杂度仍然不行,考虑删除$k-1$和删除$k$的变化,可以看作以下3步:
1.$j$的枚举范围由$[k,n]$缩小为$[k+1,n]$,将$st_{k}$向之前的贡献删去
2.查询$k$上的答案,用一个set去维护
3.$i$的枚举范围由$[1,k-1]$扩大为$[1,k]$,将$ed_{k}$向之后的贡献加入
用一个set维护插入、删除和查询最大值(注意要可重,因此删除要删指针),由于每一条边最多在左端点插入一次、右端点删除一次,总复杂度为$o(mlog_{2}m)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 500005 4 multiset<int>s; 5 queue<int>q; 6 vector<int>v[N],vi[N]; 7 int n,m,x,y,r[N],id[N],st[N],ed[N],ans[N]; 8 void add(int x,int y){ 9 r[y]++; 10 v[x].push_back(y); 11 vi[y].push_back(x); 12 } 13 void del(int k){ 14 assert(s.find(k)!=s.end()); 15 s.erase(s.find(k)); 16 } 17 int main(){ 18 scanf("%d%d",&n,&m); 19 for(int i=1;i<=m;i++){ 20 scanf("%d%d",&x,&y); 21 add(x,y); 22 } 23 for(int i=1;i<=n;i++){ 24 add(0,i); 25 add(i,n+1); 26 } 27 x=0; 28 q.push(0); 29 while (!q.empty()){ 30 int k=q.front(); 31 if ((1<=k)&&(k<=n))id[++x]=k; 32 q.pop(); 33 for(int i=0;i<v[k].size();i++) 34 if (--r[v[k][i]]==0)q.push(v[k][i]); 35 } 36 for(int i=1;i<=n;i++) 37 for(int j=0;j<vi[id[i]].size();j++)ed[id[i]]=max(ed[id[i]],ed[vi[id[i]][j]]+1); 38 for(int i=n;i;i--) 39 for(int j=0;j<v[id[i]].size();j++)st[id[i]]=max(st[id[i]],st[v[id[i]][j]]+1); 40 for(int i=1;i<=n;i++)s.insert(st[i]-1); 41 for(int i=1;i<=n;i++){ 42 for(int j=0;j<vi[id[i]].size();j++)del(ed[vi[id[i]][j]]+st[id[i]]-1); 43 if (!s.size())ans[i]=0; 44 else ans[i]=(*--s.end()); 45 for(int j=0;j<v[id[i]].size();j++)s.insert(ed[id[i]]+st[v[id[i]][j]]-1); 46 } 47 ans[0]=ans[1]; 48 for(int i=2;i<=n;i++)ans[0]=min(ans[0],ans[i]); 49 for(int i=1;i<=n;i++) 50 if (ans[i]==ans[0]){ 51 printf("%d %d",id[i],ans[i]); 52 return 0; 53 } 54 }