[bzoj3832]Rally——拓扑排序+堆

题目大意:

给定一个N个点M条边的有向无环图,每条边长度都是1。
请找到一个点,使得删掉这个点后剩余的图中的最长路径最短。

思路:

首先我们加一个超级源S和一个超级汇T,然后整个题目就变成了求\(S->T\)的最长链。计算出S到每一个点的最长路和每一个点到T的最长路,这样我们就可以很方便地算出来经过任意一条边的最长路了。
考虑到删除一个点之后新的最长链一定会经过一些边,于是我们可以维护一个集合代表删除了这个点之后新的最长路可能会经过哪些边。
假设我们删除了点u。
首先我们所维护的集合内的边一定和点u没有偏序关系,否则经过这条边的最长路可能会经过点u。
其次有偏序关系的边也没有必要重复放入集合中。
于是一个集合我们可以看成是一个割,其中的边都是平行的,不难发现图中的所有路径都经过了这个割中的边。
删除一个点之后我们将这个割中和这个点相连的所有边去掉,剩下的边集的答案便一定是去掉了这个点之后的最长路的长度。
于是接下来就只需要动态维护这个割集并使它满足上面的条件,按照拓扑序的过程,维护一个指向目前的点集的边集,每次选定一个点将所有指向它的边删除,这个集合便一定满足性质,计算完之后再将u删除,将所有从u出发的边加入集合即可。
上述的过程可以用权值线段树或堆来实现。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
	freopen("bzoj3832.in","r",stdin);
	freopen("bzoj3832.out","w",stdout);
}

template<typename T>void read(T &_){
	T __=0,mul=1; char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')mul=-1;
		ch=getchar();
	}
	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
	_=__*mul;
}

const int maxn=5e5+10;
const int maxm=2e6+10;
const int inf=0x3f3f3f3f;
int n,m,f[maxn],g[maxn];
int beg[maxn],las[maxm],to[maxm],from[maxm],cnte=1;

void add(int u,int v){
	las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; from[cnte]=u;
}

namespace dp{
	void get_f(){
		int deg[maxn]={0};
		queue<int>qu;
		REP(i,2,cnte)++deg[to[i]];
		REP(i,0,n+1)if(!deg[i])qu.push(i),f[i]=0;
		while(!qu.empty()){
			int u=qu.front(); qu.pop();
			for(int i=beg[u];i;i=las[i]){
				 int v=to[i]; --deg[v];
				 f[v]=max(f[v],f[u])+1;
				 if(!deg[v])qu.push(v);
			}
		}
	}
	void get_g(int u){
		if(g[u]!=-1)return;
		for(int i=beg[u];i;i=las[i]){
			int v=to[i];
			get_g(v);
			g[u]=max(g[u],g[v]+1);
		}
		if(g[u]==-1)g[u]=0;
	}
}

void init(){
	read(n); read(m);
	int u,v;
	REP(i,1,m)read(u),read(v),add(u,v);
	REP(i,1,n)add(0,i),add(i,n+1);

	memset(f,-1,sizeof(f));
	memset(g,-1,sizeof(g));
	dp::get_f();
	dp::get_g(0);
}

int val[maxm],deg[maxn],ans=inf,id;
queue<int>qu;
vector<int>pre[maxn];
priority_queue<pii>h;
bool del[maxm];

void work(){
	REP(i,2,cnte){
		val[i]=f[from[i]]+g[to[i]]+1;
		++deg[to[i]];
	}
	qu.push(0);
	while(!qu.empty()){
		int u=qu.front(); qu.pop();
		REP(i,0,pre[u].size()-1)
			del[pre[u][i]]=1;
		while(!h.empty() && del[h.top().se])h.pop();
		if(!h.empty() && h.top().fi<ans)ans=h.top().fi,id=u;
		for(int i=beg[u];i;i=las[i]){
			int v=to[i]; --deg[v];
			h.push(mk(val[i],i));
			pre[v].pb(i);
			if(!deg[v])qu.push(v);
		}
	}
	printf("%d %d\n",id,ans-2);
}

int main(){
	File();
	init();
	work();
	return 0;
}

posted @ 2018-10-27 14:45  ylsoi  阅读(170)  评论(0编辑  收藏  举报