一、题目
\(n\) 个点 \(m\) 条边的无向图,边有边权,有 \(q\) 个三元组 \((u,v,l)\),存在一个三元组使得存在一条路径以 \(u,v\) 为端点,长度不超过 \(l\),并且经过这条边,那么这条边就合法。求合法边的数量。
\(2\leq n\leq 600,1\leq m,q\leq\frac{n(n-1)}{2}\)
二、解法
把题目的条件翻译一下,如果一条边 \((x,y,c)\) 和三元组 \((u,v,l)\) 满足下列条件则边合法:
\[dis[u][x]+dis[v][y]+c\leq l \]\(O(n^4)\) 可以暴力算,但是我没草过去,这个东西感觉像四元环计数,所以枚举对角线上的点是很有用的,因为这样可以拆成不相关的两个部分,我们枚举 \(x,v\),那么改写一下这个判断式:
想让这个等式合法,而两边都不相关,所以可以直接找右边的最大值(枚举 \(u\)),然后枚举 \(y\) 就可以判断这个等式是否成立了,时间复杂度 \(O(n^3)\)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define int long long
const int M = 605;
const int N = M*M;
const int inf = 1e18;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,q,ans,a[N],b[N],c[M][M],d[M][M],l[M][M],ok[M][M];
signed main()
{
n=read();m=read();
memset(d,0x3f,sizeof d);
memset(c,0x3f,sizeof c);
for(int i=1;i<=n;i++)
d[i][i]=0;
for(int i=1;i<=m;i++)
{
a[i]=read();b[i]=read();
c[a[i]][b[i]]=c[b[i]][a[i]]=
d[a[i]][b[i]]=d[b[i]][a[i]]=read();
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
q=read();
while(q--)
{
int u=read(),v=read(),c=read();
l[u][v]=l[v][u]=c;
}
for(int v=1;v<=n;v++)
for(int x=1;x<=n;x++)
{
int mr=0;
for(int u=1;u<=n;u++)
mr=max(mr,l[u][v]-d[u][x]);
for(int y=1;y<=n;y++)
if(d[v][y]+c[x][y]<=mr)
ok[x][y]=ok[y][x]=1;
}
for(int i=1;i<=m;i++)
if(ok[a[i]][b[i]]) ans++;
printf("%lld\n",ans);
}