CF576 D Flights for Regular Customers (矩乘)

mac2024-02-23  43

题意

一张n个点,m条边的有向图,你要从1到n。每条边有一个限制x表示,要使用这条边你必须先经过了x条边。求最短路。

n , m ≤ 150 , x ≤ 1 e 9 n,m\leq150,x\leq1e9 n,m150,x1e9

思路

将边排序,然后枚举最终需要使用(而不是可以使用)的边集,首先考虑如何让选中的所有边可行。设 F [ i ] F[i] F[i]表示走到 i i i并且恰好解锁当前边是否可行。发现在这条边可用前,可用边集是不变的。于是可以使用上一条加入边集的边的 F F F来更新这一次的 F F F。这里需要用到矩阵快速幂来加速,并且还要使用bitset优化。把当前的可行图跑一个floyd。再枚举解锁所有选中边的点,计算长度即可。 O ( m n 3 / w log ⁡ ( d ) ) O(mn^3/w\log(d)) O(mn3/wlog(d)),CF 4s足够通过本题。 #include <bits/stdc++.h> using namespace std; const int N = 160, inf = 1e9 + N; int n, m; bool a[N][N], b[N][N], lasb[N][N]; int f[N][N]; pair<int, pair<int,int> > edge[N]; int ans = inf; bitset<N> ar[N], br[N]; void mult(const bool a[N][N], const bool b[N][N], bool c[N][N]) { for(int i = 1; i <= n; i++) { ar[i].reset(); for(int j = 1; j <= n; j++) { ar[i][j] = a[i][j]; } } for(int j = 1; j <= n; j++) { br[j].reset(); for(int i = 1; i <= n; i++) { br[j][i] = b[i][j]; } } for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { c[i][j] = (ar[i] & br[j]).any(); } } } void ksm(const bool a[N][N], int y, bool c[N][N]) { static bool f[N][N]; memcpy(f, a, sizeof f); for(; y; y>>=1) { if (y & 1) mult(c, f, c); mult(f, f, f); } } int main() { freopen("d.in","r",stdin); cin>>n>>m; for(int i = 1; i <= m; i++) { int u, v, d; scanf("%d %d %d",&u,&v,&d); edge[i] = make_pair(d, make_pair(u, v)); } sort(edge + 1, edge + 1 + m); b[1][1] = 1; for(int e = 1; e <= m; e++) { ksm(a, edge[e].first - edge[e - 1].first, b); a[edge[e].second.first][edge[e].second.second] = 1; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) if (a[i][j]) { f[i][j] = 1; } else f[i][j] = inf; } for(int k = 1; k <= n; k++) { for(int i = 1; i <= n; i++) if (i != k) { for(int j = 1; j <= n; j++) if (i != j && j != k) { f[i][j] = min(f[i][j], f[i][k] + f[k][j]); } } } for(int i = 1; i <= n; i++) if (b[1][i]) { ans = min(ans, edge[e].first + f[i][n]); } } if (ans == inf) { printf("Impossible\n"); } else printf("%d\n", ans); }
最新回复(0)