[GXOI/GZOI2019] 旅行者

题目传送门

题解

Solution \(1\)

考虑有 \(s\) 个源点与 \(t\) 个汇点,求这些源点与汇点两两之间的最小最短路距离的做法。显然可以建一个超级源点和超级汇点。

现在问题转化成如何把这些点分为若干组,使任意两个点都被分为不同的组一次。

考虑二进制分组,具体地,枚举每个二进制位,当前位为 \(0\) 的分为一组,为 \(1\) 分为另外一组。因为两个不相同的数一定有一位二进制位不相同,所以满足条件。

时间复杂度 \(O(m \times \log^2 n)\)

void dij(){
	for(int i=0;i<=n+1;i++) dis[i]=INT_MAX,vis[i]=false;
	dis[0]=0,q.push({0,0});
	while(!q.empty()){
		pr x=q.top();q.pop();
		if(vis[x.second]) continue;
		vis[x.second]=true;
		for(pr i:g[x.second]){
			int dao=i.first,val=i.second;
			if(dis[dao]>x.first+val){
				dis[dao]=x.first+val;
				q.push({dis[dao],dao});
			}
		}
	}
}

void solve(int ce){
	n=read(),m=read(),k=read();
	for(int i=1;i<=m;i++){
		int u=read(),v=read(),w=read();
		g[u].push_back({v,w});
	}
	
	for(int i=1;i<=k;i++) Q[i]=read();
	int ans=INT_MAX;
	for(int i=0;i<=15;i++){
		int x=1<<i;
		for(int j=1;j<=k;j++){
			if(Q[j]&x) g[0].push_back({Q[j],0});
			else g[Q[j]].push_back({n+1,0});
		}
		dij();ans=min(ans,dis[n+1]);
		g[0].clear();
		for(int j=1;j<=k;j++)
			if(!(Q[j]&x)) g[Q[j]].pop_back();
		
		for(int j=1;j<=k;j++){
			if(Q[j]&x) g[Q[j]].push_back({n+1,0});
			else g[0].push_back({Q[j],0});
		}
		dij();ans=min(ans,dis[n+1]);
		g[0].clear();
		for(int j=1;j<=k;j++)
			if(Q[j]&x) g[Q[j]].pop_back();
	}
	cout<<ans<<"\n";
	for(int i=1;i<=n;i++) g[i].clear();
}

Solution \(2\)

考虑最短路的产生过程,显然我们可以枚举每个边,找到所有关键点到 \(u\) 的最短路加上 \(v\) 到所有关键点的最短路再加上边权,用这个来更新答案。只需要在正图上跑一遍 dij,在反图上也跑一遍即可。

由于不能从自己到自己,所以记录当前点的最短路是从哪个关键点来的,如果 \(from_u \ne from_v\),那么可以更新答案。

证明正确性:考虑能成为答案的最短路路径,设两端分别为 \(U\)\(V\)。因为是正权图,所以其路径中一定没有别的关键点,所以一定有一个临界边,即 \(dis_{U,u}<dis_{u,V}\)\(dis_{U,v} > dis_{v,V}\),即 \(from_u=U\)\(from_v=V\)。所以一定能取到答案。

时间复杂度:\(O(m \log n)\)

struct node{
	vector<pr > g[N];
	int dis[N],f[N];
	
	bool vis[N];
	
	priority_queue<pr,vector<pr >,greater<pr > > q;
	
	void dij(){
		for(int i=1;i<=n;i++) dis[i]=INT_MAX,vis[i]=false;
		vis[0]=false,dis[0]=0,q.push({dis[0],0});
		while(!q.empty()){
			pr x=q.top();q.pop();
			if(vis[x.second]) continue;
			vis[x.second]=false;
			for(pr i:g[x.second]){
				int dao=i.first,val=i.second;
				if(dis[dao]>x.first+val){
					dis[dao]=x.first+val;
					f[dao]=mk[dao]?dao:f[x.second];
					q.push({dis[dao],dao});
				}
			}
		}
	}
}tu[2];

void solve1(int ce){
	n=read(),m=read(),k=read();
	for(int i=1;i<=m;i++){
		int u=read(),v=read(),w=read();
		tu[0].g[u].push_back({v,w});
		tu[1].g[v].push_back({u,w});
	}
	for(int i=1;i<=k;i++) Q[i]=read();
	for(int i=1;i<=k;i++)
		tu[0].g[0].push_back({Q[i],0}),
		tu[1].g[0].push_back({Q[i],0}),
		mk[Q[i]]=true;
	tu[0].dij();tu[1].dij();
	
	int ans=INT_MAX;
	for(int i=1;i<=n;i++){
		for(pr j:tu[0].g[i]){
			if(tu[0].f[i]==tu[1].f[j.first]) continue;
			ans=min(ans,tu[0].dis[i]+j.second+tu[1].dis[j.first]);
		}
	}
	cout<<ans<<"\n";
	
	for(int j=0;j<=1;j++)
		for(int i=0;i<=n;i++)
			tu[j].g[i].clear(),mk[i]=false,tu[j].f[i]=0;
}
posted @ 2025-06-06 19:00  ask_silently  阅读(9)  评论(0)    收藏  举报