CF1483D Useful Edges

一、题目

点此看题

\(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\),那么改写一下这个判断式:

\[dis[v][y]+c\leq l-dis[u][x] \]

想让这个等式合法,而两边都不相关,所以可以直接找右边的最大值(枚举 \(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);
}
上一篇:781. 森林中的兔子


下一篇:使用 EntityFramework后把一个对象序列化成json字符串引起循环引用的问题