CF 576D Flights for Regular Customers【矩阵快速幂】【图论】

正解:

将边按照d值的大小排序,从小到大依次枚举解锁当前边的时间情况。
使用三个矩阵:

\(a[i][j]表示i与j之间有无一条路径\)
\(b[i][j]表示当前能否从i点走到j点\)
\(f[i][j]表示当前从i到j的路径长度\)

其中a,b可以用bitset代替。f每一次用Floyd更新。

当枚举到一条边时,距离解锁该边还要走\(\Delta d\)步,我们就考虑在这几步中,他可以到达那些节点。以此更新b。

每走一步,a就要乘上自己一次,更新路径数量,再把a的幂与b相乘,这样更新b矩阵,注意这里a不会变,我们只使用它的值来乘方,乘方后的矩阵就是所有的走\(\Delta d\)的情况。

然后使用Floyd更新新加入的边对于路径长度的影响。

最后记录当前的\(d\),再更新a。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int n,m;
struct node{
	int u,v,d;
	bool operator < (const node &b){
		return d<b.d;
	}
}p[160];
const int N=160;
bitset<N> a[N],b[N];
int f[N][N];
void Mul(bitset<N> *a,bitset<N> *b){
	bitset<N> ret[N];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(a[i][j]){
				ret[i]|=b[j];
			}
		}
	}
	for(int i=1;i<=n;i++){
		a[i]=ret[i];
	}
	return ;
}

void Pow(bitset<N> *a,int b){
	bitset<N> ret[N];
	for(int i=1;i<=n;i++){
		ret[i][i]=1;
	}
	while(b!=0){
		if(b&1){
			Mul(ret,a);
		}
		Mul(a,a);
		b>>=1;
	}
	for(int i=1;i<=n;i++){
		a[i]=ret[i];
	}
	return ;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>p[i].u>>p[i].v>>p[i].d;
	}	
	sort(p+1,p+m+1);
	memset(f,inf,sizeof f);
	for(int i=1;i<=n;i++){//路径长度
		f[i][i]=0;
	}
	int cnt=0;
	int ans=inf;
	for(int i=1;i<=n;i++){
		b[i][i]=1;//自己可以到达自己
	}
	for(int i=1;i<=m;i++){
		int x=p[i].u,y=p[i].v,d=p[i].d;
		int delta=d-cnt;//$\Delta d$
		bitset<N> tmp[N];
		for(int i=1;i<=n;i++){
			tmp[i]=a[i];
		}
		Pow(tmp,delta);//快速幂
		Mul(b,tmp);
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n;k++){
				f[j][k]=min(f[j][k],f[j][x]+1+f[y][k]);//更新路径长度
			}
		}
		for(int j=1;j<n;j++){
			if(b[1][j]){
				ans=min(ans,d+f[j][n]);//更新答案
			}
		}
		a[x][y]=1;//更新a
		cnt=d;
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2021-11-11 21:12  SSZX_loser_lcy  阅读(36)  评论(0编辑  收藏  举报