[IOI2018]高速公路收费——二分查找+bfs

Posted khada-jhin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[IOI2018]高速公路收费——二分查找+bfs相关的知识,希望对你有一定的参考价值。

题目链接:

IOI2018highway

题目大意:给出一张$n$个点$m$条边的无向图,并给出一对未知的起点和终点,每条边都有两种边权$A$和$B$(每条边的$A$和$B$都分别相同),每次你可以设置每条边的边权并向交互库询问,交互库会返回给你当前边权下起点到终点的最短路,你需要在不多于$50$次的询问后找出起点和终点。

我们设起点为$S$,终点为$T$。

首先需要一次询问将边权都设为$A$来知道$S$到$T$的最短路。然后我们可以用二分来找到一个处于$S$到$T$最短路上的点:每次将编号在$[0,mid]$的点的所有出边设为$B$,其他的设为$A$。如果得到的最短路不变,那么显然编号在$[mid+1,n-1]$的点有处于$S$到$T$最短路上的点,反之编号在$[0,mid]$的点有处于$S$到$T$最短路上的点。我们设找到的这个点为$x$,那么$S$与$T$中一定有一个点距离$x$较远,我们设这个点为$S$。从$x$开始$bfs$,二分然后每次将$bfs$序的$[mid+1,n-1]$这些点的所有出边设为$B$,其他边设为$A$,这样就能找到$S$,再从$S$开始$bfs$同样二分$bfs$序找到$T$。这样询问次数是$3*log_{2}^{90000}+1=52$,可以得到$90$分。

既然第一步可以二分找到最短路上的一个点,那么我们同样也可以找到一条边。每次将编号在$[0,mid]$的边设为$B$其他边设为$A$来找到最短路上的一条边,对于这条边的两端点$(u,v)$显然每个点到这两个点的最短距离不同,我们按每个点到这两个点的最短距离将离$u$更近的分为一部分,离$v$更近的分为另一部分,对于每部分还是二分$bfs$序来分别找到$S$和$T$,这样最坏情况询问次数为$1+log_{2}^{130000}+2*log_{2}^{45000}=50$,即可得到满分。

#include"highway.h"
#include<queue>
#include<cstdio>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
vector<int>s[90010];
vector<int>to[90010];
int w[130010];
ll path;
queue<int>q;
int dis_u[90010];
int dis_v[90010];
int que_u[90010];
int que_v[90010];
int cnt_u;
int cnt_v;
int S,T;
bool cmp_u(int x,int y)
{
	return dis_u[x]<dis_u[y];
}
bool cmp_v(int x,int y)
{
	return dis_v[x]<dis_v[y];
}
void find_pair(int n,vector<int> u,vector<int> v,int A,int B)
{
	int num=u.size();
	for(int i=0;i<num;i++)
	{
		s[u[i]].push_back(i);
		to[u[i]].push_back(v[i]);
		s[v[i]].push_back(i);
		to[v[i]].push_back(u[i]);
	}
	path=ask(vector<int>(w,w+num));
	int l=0;
	int r=num-1;
	while(l<r)
	{
		int mid=(l+r)>>1;
		for(int i=0;i<num;i++)
		{
			w[i]=0;
		}
		for(int i=0;i<=mid;i++)
		{
			w[i]=1;
		}
		ll value=ask(vector<int>(w,w+num));
		if(value==path)
		{
			l=mid+1;
		}
		else
		{
			r=mid;
		}
	}
	q.push(u[l]);
	dis_u[u[l]]=1;
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		int len=to[now].size();
		for(int i=0;i<len;i++)
		{
			if(!dis_u[to[now][i]])
			{
				dis_u[to[now][i]]=dis_u[now]+1;
				q.push(to[now][i]);
			}
		}
	}
	q.push(v[l]);
	dis_v[v[l]]=1;
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		int len=to[now].size();
		for(int i=0;i<len;i++)
		{
			if(!dis_v[to[now][i]])
			{
				dis_v[to[now][i]]=dis_v[now]+1;
				q.push(to[now][i]);
			}
		}
	}
	for(int i=0;i<n;i++)
	{
		if(dis_u[i]<dis_v[i])
		{
			que_u[++cnt_u]=i;
		}
		else
		{
			que_v[++cnt_v]=i;
		}
	}
	sort(que_u+1,que_u+1+cnt_u,cmp_u);
	sort(que_v+1,que_v+1+cnt_v,cmp_v);
	l=1,r=cnt_u;
	while(l<r)
	{
		int mid=(l+r)>>1;
		for(int i=0;i<num;i++)
		{
			w[i]=0;
		}
		for(int i=mid+1;i<=cnt_u;i++)
		{
			int len=s[que_u[i]].size();
			for(int j=0;j<len;j++)
			{
				w[s[que_u[i]][j]]=1;
			}
		}
		ll value=ask(vector<int>(w,w+num));
		if(path==value)
		{
			r=mid;
		}
		else
		{
			l=mid+1;
		}
	}
	S=que_u[l];
	l=1,r=cnt_v;
	while(l<r)
	{
		int mid=(l+r)>>1;
		for(int i=0;i<num;i++)
		{
			w[i]=0;
		}
		for(int i=mid+1;i<=cnt_v;i++)
		{
			int len=s[que_v[i]].size();
			for(int j=0;j<len;j++)
			{
				w[s[que_v[i]][j]]=1;
			}
		}
		ll value=ask(vector<int>(w,w+num));
		if(path==value)
		{
			r=mid;
		}
		else
		{
			l=mid+1;
		}
	}
	T=que_v[l];
	answer(S,T);
}

以上是关于[IOI2018]高速公路收费——二分查找+bfs的主要内容,如果未能解决你的问题,请参考以下文章

收费站(二分+最短路)

spfa进阶使用二分+SLF(洛谷1951 收费站_NOI导刊2009提高)

[IOI2018]组合动作——构造

小编告诉你 什么是最短路径收费方法 2月6日至2月25日,湖南高速公路按最短路径收费!

湖南高速实行最短路径收费

春运期间这20天 全省高速公路按最短路径收费