【洛谷3573】[POI2014] RAJ-Rally(拓扑+堆)

点此看题面

  • 一张\(n\)个点的\(DAG\),你可以删去一个点,要求最小化剩余图中的最长路径。
  • \(n\le5\times10^5,m\le10^6\)

过去点集与未来点集

一个非常奇妙的做法。

首先我们跑一遍拓扑,预处理出到每个点为止的最长路\(d1_x\)和从每个点开始的最长路\(d2_x\),这只要简单地\(DAG\)上\(DP\)一下就结束了。

然后按照拓扑序,考虑每一个点的答案。

有一个很神奇的思路,就是我们维护一个过去点集\(A\)和一个未来点集\(B\)(也就是操作过的点集和尚未操作的点集),那么最长路径只有三种可能:

  • \(A\)内部:\(\max_{x\in A}d1_x\)。
  • \(B\)内部:\(\max_{x\in B}d2_x\)。
  • \(A,B\)之间:\(\max_{u\in A,v\in 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\n",t,d),0;
}
上一篇:如何在Apple Watch上使用私人Wi-Fi地址?


下一篇:Airtool for Mac(系统菜单栏网络工具) v2.3.1激活版