[CF1483D]Useful Edges

Useful Edges

题解

首先我们考虑如何判断一条边合法,我们定义 u , v u,v u,v之间的最短路为 d i s u , v dis_{u,v} disu,v,询问三元组的长度为 a s k u , v ask_{u,v} asku,v,记边为 ( x , y ) (x,y) (x,y),询问为 ( u , v ) (u,v) (u,v)
那么对于边 i i i,条件是
∃ j , d i s u j , x i + p a i d x i , y i + d i s y i , v j ⩽ a s k u j , v j \exists j,dis_{u_{j},x_{i}}+paid_{x_{i},y_{i}}+dis_{y_{i},v_{j}}\leqslant ask_{u_{j},v_{j}} j,disuj,xi+paidxi,yi+disyi,vjaskuj,vj
考虑移项,可得
∃ j , d i s u j , x i + p a i d x i , y i ⩽ a s k u j , v j − d i s y i , v j \exists j,dis_{u_{j},x_{i}}+paid_{x_{i},y_{i}}\leqslant ask_{u_{j},v_{j}}-dis_{y_{i},v_{j}} j,disuj,xi+paidxi,yiaskuj,vjdisyi,vj
我们可以枚举 u u u y y y,这样前面半截就只与 x x x相关,后面半截就只与 v v v相关。
那么,对于每个 ( u , y ) (u,y) (u,y),由于是存在,我们可以找到最大的 a s k u , v − d i s y , v ask_{u,v}-dis_{y,v} asku,vdisy,v,此后再枚举 x x x,如果有 d i s u , x + p a i d x , y ⩽ ( a s k u , v − d i s y , v ) max ⁡ dis_{u,x}+paid_{x,y}\leqslant (ask_{u,v}-dis_{y,v})_{\max} disu,x+paidx,y(asku,vdisy,v)max,则该边 ( x , y ) (x,y) (x,y)就是合法的。
我们只需要枚举 ( u , y ) (u,y) (u,y),算出该数对使得那些 x x x合法,最后统计合法的边数即可。
至于 d i s dis dis数组,可以先通过Floyed求出来。

时间复杂度 O ( n 3 ) O\left(n^3\right) O(n3)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 300005
#define lowbit(x) (x&-x)
#define reg register
#define mp make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const LL mo=1e9+7;
const LL inv2=5e8+4;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int n,m,q,paid[605][605],dis[605][605],ask[605][605],ans;bool vis[605][605];
struct edge{int u,v;}e[MAXN];
signed main(){
	read(n);read(m);memset(paid,0x3f,sizeof(paid));
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=m;i++){
		int u,v,w;read(u);read(v);read(w);e[i]=(edge){u,v};
		dis[u][v]=dis[v][u]=paid[u][v]=paid[v][u]=w;
	}
	for(int i=1;i<=n;i++)paid[i][i]=dis[i][i]=0;
	for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
		if(i!=j&&i!=k&&j!=k)dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]);
	//for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)printf("dis%d %d:%d\n",i,j,dis[i][j]);
	read(q);
	for(int i=1,u,v,w;i<=q;i++)read(u),read(v),read(w),ask[u][v]=ask[v][u]=w;
	for(int u=1;u<=n;u++)
		for(int y=1;y<=n;y++){
			int maxx=0;for(int v=1;v<=n;v++)maxx=max(maxx,ask[u][v]-dis[y][v]);
			for(int x=1;x<=n;x++)if(dis[u][x]+paid[x][y]<=maxx)vis[x][y]=1;
		}
	for(int i=1;i<=m;i++)if(vis[e[i].u][e[i].v]||vis[e[i].v][e[i].u])ans++;
	printf("%d\n",ans);
	return 0;
}


谢谢!!!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值