【xsy2748】 fly 矩阵快速幂
题目大意:有$n$个点,$m$条有向边,其中第$i$条边需要在$t_i$秒后才出现在图上。
有一个人刚开始呆在$1$号节点,每秒钟他必须要选择一条从他所在位置走出去的边,走出去(如果没有的话这人就死了)
问你他从$1$号点走到$n$号所需的最少时间。
数据范围:$n,m≤100,max(t_i)≤10^9$
此题貌似是一个套路题
令$vis[T][i]$表示在时刻$T$,是否能够到达$i$号节点
我们可以用$O(m)$的时间基于$vis[T][i]$求出$vis[T+1][i]$。
然而这么搞复杂度直接爆炸了。
我们把$vis[T]$看做是一个$1\times n$的矩阵,我们构造加入前i+1条边的图的邻接矩阵$A$(矩阵显然是$n\times n$的)
不难发现$vis[T+1]=vis[T]\times A$,这里的乘法是矩阵乘法。
单次矩乘的复杂度是$O(n^2)$,加入矩阵快速幂转移就是$O(n^2\log(t_{i+1}-t_i))$。
然而这么求我们只会求出$vis$数组的某一些项,然而某条边被加入后是一直存在的。
不难发现我们只需要在时刻$t_{i+1}$的基础上再走上$n$步,就可以知道是否可以在加入这条边后到达终点(结论显然)
然后就没有了
时间复杂度:$O(n^3\log T)$
1 #include<bits/stdc++.h> 2 #define M 105 3 #define INF 1234567890 4 using namespace std; 5 6 struct mat{ 7 bool a[M][M]; int n,m; 8 mat(){memset(a,0,sizeof(a));} 9 mat(int nn,int mm){memset(a,0,sizeof(a)); n=nn; m=mm;} 10 void set1(){memset(a,0,sizeof(a)); for(int i=1;i<=n;i++) a[i][i]=1;} 11 friend mat operator *(mat a,mat b){ 12 mat c=mat(a.n,b.m); 13 for(int i=1;i<=a.n;i++) 14 for(int k=1;k<=b.n;k++) if(a.a[i][k]) 15 for(int j=1;j<=a.m;j++) 16 c.a[i][j]|=a.a[i][k]&b.a[k][j]; 17 return c; 18 } 19 friend mat operator ^(mat a,int b){ 20 mat ans=mat(a.n,a.m); ans.set1(); 21 while(b){ 22 if(b&1) ans=ans*a; 23 b=b>>1; a=a*a; 24 } 25 return ans; 26 } 27 }a,b; 28 struct edge{ 29 int u,v,t; 30 void rd(){scanf("%d%d%d",&u,&v,&t);} 31 friend bool operator <(edge a,edge b){return a.t<b.t;} 32 }p[M]; 33 int n,m,ans=INF; 34 int main(){ 35 scanf("%d%d",&n,&m); 36 a=mat(1,n); b=mat(n,n); a.a[1][1]=1; 37 if(n==1) {printf("0\n"); return 0;} 38 for(int i=1;i<=m;i++) p[i].rd(); 39 sort(p+1,p+m+1); 40 for(int i=1;i<=m;i++){ 41 mat hh=a; 42 b.a[p[i].u][p[i].v]=1; 43 for(int j=1;j<=n;j++){ 44 hh=hh*b; 45 if(hh.a[1][n]){ 46 ans=min(ans,p[i].t+j); 47 break; 48 } 49 } 50 if(i<m) a=a*(b^(p[i+1].t-p[i].t)); 51 } 52 if(ans==INF) printf("Impossible\n"); 53 else printf("%d\n",ans); 54 }