CF576D Flights for Regular Customers
题意
给出一张有向图,对于边\(i\)有限制条件\(d_i\),表示在走边\(i\)前必须走过至少\(d_i\)条其他的边。为从\(1\)到\(n\)最少要走几条边。
无解输出\(Impossible\)
\(n \leq 150,m \leq 150,d_i \leq 10^9\)
思路
特别棒的一篇
首先按照\(d\)的大小升序排序
然后分成\(m\)个时刻处理,\(can[i][j]\)表示当前时刻\(i\)能否走到\(j\)
更新\(can\)即通过上一次时刻乘上邻接矩阵的\(d_i-d_{last}\)次方(注意邻接矩阵不断变化,每次处理完再加上一条边)
更新答案的时候,我们枚举每个点。
若\(1\)可以走到\(i\)点,则更新答案为\(d_i(当前时刻走到i)+dis[i][n](从i还要走的路)\)。
最短路由\(floyd\)求解即可。考虑到d很大,用矩阵快速幂加速。
矩阵乘法的时候用\(bitset\)优化。
#include <bits/stdc++.h>
using namespace std;
const int N=160;
int ans=0x3f3f3f3f,n,m,dis[N][N],last;
bitset<N> can[N],a[N],t[N];
struct edge{
int x,y,d;
}e[N];
bool cmp(edge x,edge y){
return x.d<y.d;
}
void mul(bitset<N> *a,bitset<N> *b){
bitset<N> tt[N];
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (a[i][j]) tt[i]|=b[j];
for (int i=1;i<=n;i++) a[i]=tt[i];
}
void ksm(bitset<N> *a,int y){
bitset <N> Ans[N];
for (int i=1;i<=n;i++) Ans[i][i]=1;
for (;y;y>>=1,mul(a,a))
if (y&1) mul(Ans,a);
for (int i=1;i<=n;i++) a[i]=Ans[i];
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].d);
sort(e+1,e+m+1,cmp);
memset(dis,0x3f,sizeof(dis));
for (int i=1;i<=n;i++) dis[i][i]=0,can[i][i]=1;
for (int i=1;i<=m;i++){
int x=e[i].x,y=e[i].y,d=e[i].d;
for (int j=1;j<=n;j++) t[j]=a[j];
ksm(t,d-last);
mul(can,t);
for (int j=1;j<=n;j++)
for (int k=1;k<=n;k++)
dis[j][k]=min(dis[j][k],dis[j][x]+1+dis[y][k]);
for (int j=1;j<n;j++) if (can[1][j]) {
ans=min(ans,d+dis[j][n]);
}
last=d;
a[x][y]=1;
}
if (ans<0x3f3f3f3f) printf("%d\n",ans);
else puts("Impossible");
return 0;
}
后记
就是不停的挂,比如什么\(i,j\)或者\(n,N\)写反
还有\(ans\)的初始化不能太大也不能太小
* 生而自由 爱而无畏 *