2021牛客暑期多校训练营7 xay loves Floyd

题目

在这里插入图片描述

分析

这题目听说出题人是用 l o w b i t lowbit lowbit写的,但是我们可以在题目给的错误的 F l o y d Floyd Floyd算法的基础上进行优化。
首先,我们可以先用 d i j k s t r a dijkstra dijkstra算法来算出正确的最短路大小。
然后对题目给的错误的 F l o y d Floyd Floyd算法进行剪枝。可以发现,对于点对 ( u , v ) (u,v) (u,v)来说,如果 d i s u , v = = i n f dis_{u,v}==inf disu,v==inf,那么用题目给的算法做出来的 d i s u , v dis_{u,v} disu,v也等于 i n f inf inf,那么对于 d i s u , v = = i n f dis_{u,v}==inf disu,v==inf ( u , v ) (u,v) (u,v),我们可以直接 a n s + + ans++ ans++。而且,如果用题目给的算法跑到一半发现某一点对的距离是正确的,那么也可以减掉,因为这个正确的值一定是最小值,跑出来的错误答案一定是比正确答案大的。
但是,这样的复杂度还不够,我们要继续剪枝。这里引入一个有效边,就是最短路经过的边。然后预处理一些直接连接两点的边,再在求 a n s ans ans过程中更新,就可以将时间压缩到 O ( n m ) O(nm) O(nm)

#include<bits/stdc++.h>
#define ll long long
#define pll pair<ll,ll>
using namespace std;
ll dp[2005][2005],a[2005][2005],r[2005][2005],n,m;
vector<pll>v[2005];
const ll inf=1<<30;
void dij(ll x)
{
	priority_queue<pll,vector<pll>,greater<pll> >q;
	q.push(make_pair(0,x));
	a[x][x]=0;
	while(!q.empty())
	{
		pll now=q.top();
		q.pop();
		for (ll i=0;i<v[now.second].size();i++)
		{
			ll y=v[now.second][i].first;
			ll z=v[now.second][i].second;
			if (a[x][y]>a[x][now.second]+z)
			{
				a[x][y]=a[x][now.second]+z;
				q.push(make_pair(a[x][y],y));
			}
		}
	}
}
vector<ll>q[2005];
int main()
{
	scanf("%lld%lld",&n,&m);
	for (ll i=1;i<=m;i++)
	{
		ll x,y,z;
		scanf("%lld%lld%lld",&x,&y,&z);
		v[x].push_back(make_pair(y,z));
	}
	for (ll i=0;i<=2000;i++)
	 for (ll j=0;j<=2000;j++)
	  a[i][j]=inf;
	for (ll i=1;i<=n;i++) dij(i);
	for (ll i=1;i<=n;i++)
	 for (ll j=0;j<v[i].size();j++)
	  if (a[i][v[i][j].first]==v[i][j].second)
	   r[i][v[i][j].first]=1,q[i].push_back(v[i][j].first);
	ll ans=0;
	for (ll i=1;i<=n;i++)
	 for (ll j=1;j<=n;j++)
	  if (r[i][j]||i==j||a[i][j]==inf) ans++;
	  else
	  {
	  	for (ll k=0;k<q[i].size();k++)
	  	 if (r[q[i][k]][j]&&a[i][j]==a[i][q[i][k]]+a[q[i][k]][j])
	  	 {
	  	 	ans++;
	  	 	q[i].push_back(j);
	  	 	r[i][j]=1;
	  	 	break;
	  	 }
	  }
	printf("%lld\n",ans); 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值