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,负环)的主要内容,如果未能解决你的问题,请参考以下文章