The Preliminary Contest for ICPC Asia Nanjing 2019 H. Holy Grail(spfa或floyd,负环)

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了The Preliminary Contest for ICPC Asia Nanjing 2019 H. Holy Grail(spfa或floyd,负环)相关的知识,希望对你有一定的参考价值。

LINK

题意

n n n个点, m m m条有向边,然后给你六组起始点( s s s点和 t t t点)

你要在 s s s t t t之间建一个有向边,添加后不能存在负循环

要使得权值最小,问这六组边依次是多少?(先前增加的边在后续不会消失)

(可以添加负边,题目给的边权也有可能为负)


暴力一点,显然加的边权具有单调性,越小越容易成为负环

就直接二分边权,然后从 s s s s p f a spfa spfa判断是否有负环即可

然后判负环也可以使用 f l o y d floyd floyd,就是判断一下 d i s i , i dis_{i,i} disi,i是否为负数

但是每次二分都跑一次显然超时,我们可以插点,只把 s , t s,t s,t作为中介点来跑 f l o y d floyd floyd

这样总体复杂度是 O ( n 3 + 6 ∗ 2 ∗ n 2 ∗ l o g ( i n f ) ) O(n^3+6*2*n^2*log(inf)) O(n3+62n2log(inf))

[SPFA二分]

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int inf = 5e11;
const int maxn = 2e5+10;
int n,m;
struct edge
{
	int to,nxt,w;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int w)
{
	d[++cnt] = ( edge ){v,head[u],w}, head[u] = cnt;
}
int dis[maxn],vis[maxn],num[maxn];
bool spfa(int s)
{
	for(int i=0;i<=n;i++)	dis[i] = inf, vis[i] = num[i] = 0;
	queue<int>q; q.push( s ); dis[s] = 0; num[s] = 1;
	while( !q.empty() )
	{
		int u = q.front(); q.pop();
		vis[u] = 0;
		for(int i=head[u];i;i=d[i].nxt )
		{
			int v = d[i].to;
			if( dis[u]+d[i].w<dis[v] )
			{
				dis[v] = dis[u]+d[i].w;
				if( !vis[v] )
				{
					vis[v] = 1; q.push( v );
					if( ++num[v]==n )	return false;
				}
			}
		}
	}
	return true;
}
signed main()
{
	int T; cin >> T;
	while( T-- )
	{
		cin >> n >> m;
		for(int i=1;i<=m;i++)
		{
			int l,r,w; scanf("%lld%lld%lld",&l,&r,&w);
			add(l,r,w);
		}
		for(int i=1;i<=6;i++)
		{
			int s,t; scanf("%lld%lld",&s,&t);
			int l = -inf, r = inf, ans = 0;
			add( s,t,0 );
			while( r>=l )
			{
				int mid = l+r>>1;
				d[cnt].w = mid;
				if( spfa(s) )	r = mid-1, ans = mid;
				else	l = mid+1;
			}
			d[cnt].w = ans;
			cout << ans << endl;
		}
		cnt = 1;
		for(int i=0;i<=n;i++)	vis[i] = num[i] = head[i] = 0;
	}
} 

[floyd二分]

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int inf = 5e11;
const int maxn = 2e5+10;
int n,m;
int dis[309][309],ti[309][309];;
bool floyd(int s,int t)
{
	for(int i=0;i<n;i++)
	for(int j=0;j<n;j++)
		ti[i][j] = dis[i][j];
	for(int i=0;i<n;i++)
	for(int j=0;j<n;j++)
		ti[i][j] = min( ti[i][j],ti[i][s]+ti[s][j] );
	for(int i=0;i<n;i++)
	for(int j=0;j<n;j++)
		ti[i][j] = min( ti[i][j],ti[i][t]+ti[t][j] );
	for(int i=0;i<n;i++)
		if( ti[i][i]<0 )	return false;
	return true;
}
signed main()
{
	int T; cin >> T;
	while( T-- )
	{
		cin >> n >> m;
		for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
		{
			if( i==j )	dis[i][j] = 0;
			else	dis[i][j] = inf;
		}
		for(int i=1;i<=m;i++)
		{
			int l,r,w; scanf("%lld%lld%lld",&l,&r,&w);
			dis[l][r] = w;
		}
		for(int k=0;k<n;k++)
		for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			dis[i][j] = min( dis[i][j],dis[i][k]+dis[k][j] );
		for(int i=1;i<=6;i++)
		{
			int s,t; scanf("%lld%lld",&s,&t);
			int l = -inf, r = inf, ans = 0;
			while( r>=l )
			{
				int mid = l+r>>1;
				dis[s][t] = mid;
				if( floyd(s,t) )	r = mid-1, ans = mid;
				else	l = mid+1;
			}
			dis[s][t] = ans; floyd(s,t);
			for(int j=0;j<n;j++)
			for(int q=0;q<n;q++)
				dis[j][q] = ti[j][q];
			cout << ans << endl;	
		}
	}
} 

简单的

然而根本不需要二分,只需要从 t t t s s s跑最短路,设边权为 k k k

这么新加入的边权设置为 − k -k k即可.

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int inf = 5e11;
const int maxn = 2e5+10;
int n,m;
struct edge
{
	int to,nxt,w;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int w)
{
	d[++cnt] = ( edge ){v,head[u],w}, head[u] = cnt;
}
int dis[maxn],vis[maxn];
bool spfa(int s)
{
	for(int i=0;i<=n;i++)	dis[i] = inf, vis[i] = 0;
	queue<int>q; q.push( s ); dis[s] = 0;
	while( !q.empty() )
	{
		int u = q.front(); q.pop();
		vis[u] = 0;
		for(int i=head[u];i;i=d[i].nxt )
		{
			int v = d[i].to;
			if( dis[u]+d[i].w<dis[v] )
			{
				dis[v] = dis[u]+d[i].w;
				if( !vis[v] )	vis[v] = 1,q.push( v );
			}
		}
	}
	return true;
}
signed main()
{
	int T; cin >> T;
	while( T-- )
	{
		cin >> n >> m;
		for(int i=1;i<=m;i++)
		{
			int l,r,w; scanf("%lld%lld%lld",&l,&r,&w);
			add(l,r,w);
		}
		for(int i=1;i<=6;i++)
		{
			int s,t; scanf("%lld%lld",&s,&t);
			spfa(t);
			add( s,t,-dis[s] );
			cout << d[cnt].w << endl;
		}
		cnt = 1;
		for(int i=0;i<=n;i++)	vis[i] = head[i] = 0;
	}
} 

以上是关于The Preliminary Contest for ICPC Asia Nanjing 2019 H. Holy Grail(spfa或floyd,负环)的主要内容,如果未能解决你的问题,请参考以下文章

The Preliminary Contest for ICPC Asia Yinchuan 2019

The Preliminary Contest for ICPC Asia Shenyang 2019

The Preliminary Contest for ICPC Asia Shanghai 2019

The Preliminary Contest for ICPC Asia Shanghai 2019

The Preliminary Contest for ICPC Asia Xuzhou 2019

The Preliminary Contest for ICPC Asia Nanchang 2019