BZOJ3832: [Poi2014]Rally

BZOJ3832: [Poi2014]Rally

https://lydsy.com/JudgeOnline/problem.php?id=3832

分析:

  • 新建源点向所有点连边,所有点向汇点连边。
  • \(f\)表示到这个点的最长路,\(g\)表示从这个点出发的最长路。
  • 那么一条边\((u,v)\)的贡献就是\(f_u+g_v+1\)
  • 考虑用堆维护所有的边。堆中一开始无边。
  • 按拓扑序删点,删一个点之前删掉连向它的边,统计答案后再加入它连出的边。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
#define N 1000050
template <typename T> void chkmax(T &x,T y) {if(x<y) x=y;}
template <typename T> void chkmin(T &x,T y) {if(y<x) x=y;}
int head[N],to[N<<1],nxt[N<<1],cnt,n,m,Q[N],du[N],f[N],g[N],vis[N];
priority_queue<int>q;
vector<int>v[N];
inline void add(int u,int v) {
	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
}
int main() {
	scanf("%d%d",&n,&m);
	int s=n+1,t=n+2;
	int i,x,y,l=0,r=0;
	for(i=1;i<=m;i++) scanf("%d%d",&x,&y),add(x,y),du[y]++,v[y].push_back(x);
	for(i=1;i<=n;i++) add(s,i),add(i,t),v[i].push_back(s),v[t].push_back(i),du[i]++,du[t]++;
	Q[r++]=s;
	while(l<r) {
		x=Q[l++]; 
		for(i=head[x];i;i=nxt[i]) {
			du[to[i]]--;
			chkmax(f[to[i]],f[x]+1);
			if(!du[to[i]]) Q[r++]=to[i];
		}
	}
	for(y=r-1;y>=0;y--) {
		x=Q[y]; 
		int lim=v[x].size();
		for(i=0;i<lim;i++) {
			chkmax(g[v[x][i]],g[x]+1);
		}
	}
	int ans=1<<30,p=0;
	for(y=0;y<r;y++) {
		x=Q[y];
		int lim=v[x].size();
		for(i=0;i<lim;i++) {
			vis[f[v[x][i]]+g[x]+1]++;	
		}
		while(!q.empty()&&vis[q.top()]) vis[q.top()]--,q.pop();
		if(!q.empty()) {
			if(ans>q.top()) {
				ans=q.top(); p=x;
			}
		}
		for(i=head[x];i;i=nxt[i]) q.push(f[x]+g[to[i]]+1);
	}
	printf("%d %d\n",p,ans-2);
}
posted @ 2019-01-01 17:00  fcwww  阅读(113)  评论(0编辑  收藏  举报