题目
分析
这题目听说出题人是用
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);
}