P3573 [POI2014]RAJ-Rally

P3573 [POI2014]RAJ-Rally

题意:
给定一个N个点M条边的有向无环图,每条边长度都是1。
请找到一个点,使得删掉这个点后剩余的图中的最长路径最短。
N , M ( 2 < = N < = 500000 , 1 < = M < = 1000000 ) N,M(2<=N<=500 000,1<=M<=1 000 000) N,M(2<=N<=500000,1<=M<=1000000)

思路:
转化一下问题,给定 一张DAG,对于每个点计算,删除该点,图上的最长路,对所有点的答案取min即可。

首先分析问题:
可以先找到原图中的最长路,那么删除最长路之外的点,并不会影响原图的最长路;
接下来就是删除最长路上的点:
这有两种情况,一、删除的点使原图的连通性不变,二、删除点使原图的连通性发生改变了。
对于第二种情况比较好处理,就从直径的端点跑一次正反bfs即可。
难处理的是 第一种情况,比较复杂,读者自行思考233

另一个方案:
由于是 DAG,首先想到拓扑排序。先对 DAG 拓扑排序一遍,然后求出分别以 i i i 号点为开头与结尾的最长路径 S i S_i Si T i T_i Ti。那么对于原图中的一条边 u → v u→v uv,经过这条边的最长路径的长度为 S u + T v + 1 S_u + T_v + 1 Su+Tv+1。这样删点就可以看成删除一条条边。
然后考虑如下性质:对于一个点 i i i,设拓扑序在其之前的点 点集为 S S S,拓扑序在其之后的点 点集为 T T T。那么最长路 就分为三种情况:
一、以 u u u为终点的 最长路, u ∈ S u∈S uS
二、以 v v v为起点的 最长路, v ∈ T v∈T vT
三、过 u − > v u->v u>v这条边的最长路, u ∈ S u∈S uS, v ∈ T v∈T vT

那么考虑 不经过点 i i i的最长路,假设当前点还在 T T T集合中,那么当前所有路径中 不合法的路径 只有两种:
一、以 i i i为起点的最长路
二、经过 u − > i u->i u>i这条边的最长路

把所有可能的最长路 去掉这两种不合法的情况,剩下的最长路就是 不经过点 i i i的最长路,即删除点 i i i 的最长路。

因此,我们按照 拓扑序来枚举点,初始所有点都在集合 T T T中。在得到删除点 i i i的最长路之后,需要维护 S S S T T T集合,即 将点 i i i加入到 S S S集合中,并且 加入所有经过 i i i的出边的最长路 到合法路径集合。

维护这个信息,需要做到插入一个数、删除一个数、查询最大值,可以用权值线段树,也可以用 multiset。

详情见代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const ll mod=998244353;

ll n,m;
struct node{
	int s,e,next;
}z1[1000050],z2[1000050];
ll len1,len2;
ll head1[500050],head2[500050];
int du1[500050],du2[500050];
void add1(ll a,ll b){
	z1[len1].s=a;
	z1[len1].e=b;
	z1[len1].next=head1[a];
	head1[a]=len1++;
}
void add2(ll a,ll b){
	z2[len2].s=a;
	z2[len2].e=b;
	z2[len2].next=head2[a];
	head2[a]=len2++;
}
int f[500050],g[500050];
queue<int>q;
int id[500050];//topo序 
void solve1(){
	int cnt=0;
	for(int i=1;i<=n;i++){
		if(du1[i]==0)q.push(i);
	}
	while(!q.empty()){
		int x=q.front();q.pop();
		id[++cnt]=x;
		for(int i=head1[x];i!=-1;i=z1[i].next){
			int to=z1[i].e;
			du1[to]--;
			f[to]=max(f[to],f[x]+1);
			if(du1[to]==0){
				q.push(to);
			}
		}
	}
}
void solve2(){
	for(int i=1;i<=n;i++){
		if(du2[i]==0)q.push(i);
	}
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=head2[x];i!=-1;i=z2[i].next){
			int to=z2[i].e;
			du2[to]--;
			g[to]=max(g[to],g[x]+1);
			if(du2[to]==0){
				q.push(to);
			}
		}
	}
}
int tr[2000060],res[500050];
void update(int p,int l,int r,int x,int v){
	if(l==r){
		res[x]+=v;
		if(res[x]>0)tr[p]=l;
		else tr[p]=-1;
		return ;
	}
	int mid=l+r>>1;
	if(x<=mid)update(2*p,l,mid,x,v);
	else update(2*p+1,mid+1,r,x,v);
	tr[p]=max(tr[2*p],tr[2*p+1]);
}

int main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)head1[i]=-1,head2[i]=-1;
	for(int i=1;i<=m;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		add1(a,b);add2(b,a);
		du1[b]++;du2[a]++;
	}
	solve1();solve2();
	for(int i=1;i<=n;i++){
		update(1,0,n,g[i],1);
	}
	int ans=n+1,idd;
	for(int i=1;i<=n;i++){
		int x=id[i];
		for(int j=head2[x];j!=-1;j=z2[j].next){
			int to=z2[j].e;
			update(1,0,n,f[to]+g[x]+1,-1);
		}
		update(1,0,n,g[x],-1);
		if(tr[1]<ans)ans=tr[1],idd=x;
		for(int j=head1[x];j!=-1;j=z1[j].next){
			int to=z1[j].e;
			update(1,0,n,f[x]+g[to]+1,1);
		}
		update(1,0,n,f[x],1);
	}
	printf("%d %d\n",idd,ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值