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;
}