CF1253F Cheap Robot

Description

给你一张 N 个点m条边的无向连通图,图中有 k 个充电中心。

一个机器人在图中行走,假设机器人的电池容量为 c,则任何时刻,机器人的电量 x 都必须满足 0 <= x <= c 。(机器人到达充电中心时可以充满电)

有q个询问,每次询问机器人要从a点到达b点,电池容量至少为多少,各个询问相互独立。


Solution

奇妙技巧+1
建议参考:这里

直接搞是绝对八星的。
因为我们要使路径中经过的边的权值最大值最小,即我们需要频繁地在充电中心之间移动以充电。也就是说为了使最大值最小,我们经过任意一条边时必然以离这条边最近的两个充电中心作为起点和终点。

设点x离最近的充电中心的距离为 d i s x dis_x disx
则对于边(x,y,z)来说,它造成的贡献是以最近的两个充电中心为起、终点的一条通路,耗费为 d i s x + z + d i s y dis_x+z+dis_y disx+z+disy(x,y为端点,z为该边花费)。

即我们可以将原图简化成只有k个充电中心和每条边对其最近的两个充电中心a,b贡献的通路(a,b,c)(c为权值)。
这样就可以简化原图了。

接下来对于q个询问(a,b),我们将所有通路以c为关键字排序,离线一条一条将通路加入空图内,判断在加上新通路有哪些询问满足条件记录答案即可。

在这个过程中可以采用按秩合并与kruskal重构树,这里代码给出的是按秩合并。


Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct data{
	int y,z,nxt;
}e[600010];
struct edge{
	int x,y;
	ll z;
}a[300010];
struct qwq{
	ll dis;
	int x;
};
struct que{
	int x,y,id;
}b[300010];
vector<que>v[200010];
bool operator <(const qwq &u,const qwq &v){
	return u.dis>v.dis;
}
ll ans[300010];
ll dis[200010];
int siz[200010];
int head[200010];
bool vis[200010];
int f[200010];
priority_queue<qwq>q;
int n,m,k,Q,cnt;
void add(int x,int y,ll z){
	cnt++;
	e[cnt].y=y;
	e[cnt].z=z;
	e[cnt].nxt=head[x];
	head[x]=cnt;
}
bool cmp(edge x,edge y){
	return x.z<y.z;
}
int find(int x){
	return x!=f[x]?f[x]=find(f[x]):x;
}
void dijkstra(){
	for(int i=1;i<=k;i++){
		q.push((qwq){0,i});
		dis[i]=0;
	}
	while(!q.empty()){
		qwq x=q.top(); 
		q.pop(),vis[x.x]=0;
		for(int i=head[x.x];i;i=e[i].nxt){
			int y=e[i].y,z=e[i].z;
			if(dis[y]>dis[x.x]+z){
				dis[y]=dis[x.x]+z;
				if(!vis[y]){
					q.push((qwq){dis[y],y});
					vis[y]=true;
				}
			}
		}
	}
	//for(int i=1;i<=n;i++)
	//cout<<"dis["<<i<<"]:"<<dis[i]<<endl;
	//cout<<endl;
}
int main(){
	int x,y,z;
	scanf("%d%d%d%d",&n,&m,&k,&Q);
	for(int i=1;i<=n;i++){
		dis[i]=1e17;
		f[i]=i,siz[i]=1;
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].z);
		add(a[i].x,a[i].y,a[i].z);
		add(a[i].y,a[i].x,a[i].z);
	}
	dijkstra();
	for(int i=1;i<=m;i++)
	a[i].z+=dis[a[i].x]+dis[a[i].y];//,cout<<i<<" "<<a[i].z<<endl;
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=Q;i++){
		b[i].id=i;
		scanf("%d%d",&b[i].x,&b[i].y);
		v[b[i].x].push_back(b[i]);
		v[b[i].y].push_back(b[i]);
	}
	for(int i=1;i<=m;i++){
		int x=a[i].x,y=a[i].y;
		ll z=a[i].z;
		int fx=find(x),fy=find(y);
		if(fx==fy) continue;
		if(siz[fx]<siz[fy]) swap(fx,fy);
		f[fy]=fx,siz[fx]+=siz[fy];
		for(int j=0;j<v[fy].size();j++){
			if(find(v[fy][j].x)==find(v[fy][j].y)&&!ans[v[fy][j].id])
			ans[v[fy][j].id]=z;//,cout<<"shit\n";
			else v[fx].push_back(v[fy][j]);
		}
	}
	for(int i=1;i<=Q;i++)
	printf("%lld\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值