Codeforces 576D. Flights for Regular Customers 题解

题目链接:D. Flights for Regular Customers

题目大意:给定一个\(n\)个点\(m\)条边的有向图,第\(i\)条边只有再你之前经过了\(d_i\)条边之后才可以通过,求从\(1\)点到\(n\)号点的最短距离。


题解:因为边的\(d_i\)限制很麻烦,所以先考虑除掉这个限制,可以将所有边按照\(d_i\)排序后依次加入到原图中去,这样就去掉了这个限制。

那么处理答案可以先找出所有用\(d_i\)步可以到达的点,然后对整张图跑一边 bfs 求出最小的答案。

那么怎么找出\(d_i\)步可以到达的点的个数呢?可以将边集用邻接矩阵来处理,然后跑一遍矩阵快速幂即可。好了,这一题做完了。

算一下时间复杂度是\(O(n^3m\text{log}d)\)的,这个数据范围虽然小,不过应该过不掉(如果卡常过掉的请受我一拜),可是这个算法怎么优化呢?似乎没有办法优化了,这是我们想起一句极为经典话:“智商不够,压位来凑。”注意到转移矩阵只有\(01\)两个值,所以可以用 bitset 来优化,那么邻接矩阵就需要反过来,即原来\(u-->v\)的边要反向变为\(v-->u\)的边,否则再快速幂时很难优化。时间复杂度为\(O(\frac{n^3m\text{log}d}{\omega})\)

下面是代码:

#include <queue>
#include <bitset>
#include <cstdio>
#include <algorithm>
using namespace std;
const int Maxn=150;
const int Maxm=150;
const int Inf=0x3f3f3f3f;
int n,m;
struct Edge{
	int u,v;
	int d;
	friend bool operator <(Edge p,Edge q){
		return p.d<q.d;
	}
}edge[Maxm+5];
int dis[Maxn+5];
struct Matrix{
	bitset<Maxn+5> a[Maxn+5];
	friend bitset<Maxn+5> operator *(bitset<Maxn+5> a,Matrix b){
		bitset<Maxn+5> ans;
		for(int i=0;i<n;i++){
			ans[i]=(a&b.a[i]).any();
		}
		return ans;
	}
	friend Matrix operator *(Matrix a,Matrix b){
		Matrix ans;
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
				if(a.a[i][j]){
					ans.a[i]=ans.a[i]|b.a[j];
				}
			}
		}
		return ans;
	}
}a;
void quick_power(Matrix a,int b,bitset<Maxn+5> &ans){
	while(b){
		if(b&1){
			ans=ans*a;
		}
		a=a*a;
		b>>=1;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].d);
		edge[i].u--;
		edge[i].v--;
	}
	sort(edge+1,edge+1+m);
	int last=0;
	bitset<Maxn+5> vis;
	vis[0]=1;
	int ans=Inf;
	for(int i=1;i<=m;i++){
		if(edge[i].d>=ans){
			break;
		}
		quick_power(a,edge[i].d-last,vis);
		last=edge[i].d;
		queue<int> q;
		a.a[edge[i].v][edge[i].u]=1;
		for(int j=0;j<n;j++){
			if(vis[j]){
				dis[j]=0;
				q.push(j);
			}
			else{
				dis[j]=Inf;
			}
		}
		while(!q.empty()){
			int u=q.front();
			q.pop();
			for(int v=0;v<n;v++){
				if(a.a[v][u]){
					if(dis[v]==Inf){
						dis[v]=dis[u]+1;
						q.push(v);
					}
				}
			}
		}
		ans=min(ans,dis[n-1]+edge[i].d);
	}
	if(ans==Inf){
		puts("Impossible");
	}
	else{
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2020-03-06 22:44  with_hope  阅读(197)  评论(0编辑  收藏  举报