题目传送门
有趣的题。
首先容易想到 \(f_{i,j}\) 表示到 \(i\) 走 \(j\) 步是否合法,转移也是容易的,只要 \(j \ge d_z\),那么便可以走第 \(z\) 条边。复杂度 \(Vn\)。
套路的考虑矩阵快速幂,注意到转移矩阵的变化量是 \(O\left(m\right)\) 级别的,我们按边权排序,每次跑 \(d_i-d_{i-1}\) 次,然后就可以快速跑了。为了方便,我们可以连一条 \(\left(n,n\right)\) 边权为 \(0\) 的边,然后我们在第一次求出 \(f_n = 1\) 时在区间里二分就可求出答案,复杂度 \(m\times n^3\log+n^3\times \log\),还是不好通过。
考虑我们本质只需要知道 \(0/1\) 值,我们尝试用 bitset 优化这个东西。
初始的转移是 \(a_{i,j} \vee c_{i,z}\times c_{z,j}\),转化为如果 \(c_{i,z} = 1\),那么 \(a_{i,j} \vee c_{z,j}\),第二维一样直接或即可。
复杂度同除 \(\omega\),可以通过。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define getchar() (p1 == p2 && (p2 = (p1 = buf1) + fread(buf1, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf1[1 << 23], *p1 = buf1, *p2 = buf1, ubuf[1 << 23], *u = ubuf;
namespace IO
{template<typename T>void read(T &_x){_x=0;int _f=1;char ch=getchar();while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();_x*=_f;}template<typename T,typename... Args>void read(T &_x,Args&...others){Read(_x);Read(others...);}const int BUF=20000000;char buf[BUF],to,stk[32];int plen;#define pc(x) buf[plen++]=x#define flush(); fwrite(buf,1,plen,stdout),plen=0;template<typename T>inline void print(T x){if(!x){pc(48);return;}if(x<0) x=-x,pc('-');for(;x;x/=10) stk[++to]=48+x%10;while(to) pc(stk[to--]);}
}
using namespace IO;
const int N = 160;
int n,m,x,l,r,mid,ans = 1e15;
bitset<N>v[N],v1[N],c[N],f,F,g;
struct w
{int x,y,z;
}a[N];
inline bool cmp(w x,w y){ return x.z < y.z; }
inline void Matrix_D()
{for(int i = 1;i <= n;i++) c[i] = v1[i],v1[i].reset();/*for(int i = 1;i <= n;i++)for(int j = 1;j <= n;j++)for(int z = 1;z <= n;z++)v[i][j] |= (c[i][z]&c[z][j]);*/for(int i = 1;i <= n;i++)for(int z = 1;z <= n;z++)if(c[i][z])//拉出来 v1[i] |= c[z];
}
inline void M_Q()
{g = f; f.reset();for(int i = 1;i <= n;i++)for(int j = 1;j <= n;j++)if(g[i] && v1[i][j])f[j] = 1;
}
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);read(n),read(m);for(int i = 1;i <= m;i++) read(a[i].x),read(a[i].y),read(a[i].z); a[++m].x = n,a[m].y = n,a[m].z = 0;a[++m].x = n,a[m].y = n,a[m].z = 2e9;//随便给一条不影响的边 sort(a+1,a+1+m,cmp); F[1] = 1;for(int i = 1;i <= m;i++){x = a[i].z-a[i-1].z;for(int j = 1;j <= n;j++) v1[j] = v[j]; f = F;while(x){if((x&1)) M_Q();x >>= 1,Matrix_D();}// cout<<a[i].z-a[i-1].z<<" "<<f[n]<<" &&& "<<a[i].z<<'\n';if(f[n] == 1)//这里面刚好可以 {l = 1,r = a[i].z-a[i-1].z;while(l <= r){mid = ((l+r)>>1); x = mid;for(int j = 1;j <= n;j++) v1[j] = v[j]; f = F;// for(int j = 1;j <= n;j++) cout<<f[j]<<" "; cout<<'\n';while(x){if((x&1)) M_Q();x >>= 1,Matrix_D();}if(f[n]) r = mid-1,ans = mid;else l = mid+1;} ans += a[i-1].z;break;}v[a[i].x][a[i].y] = 1; F = f;}if(ans == 1e15) cout<<"Impossible";else print(ans); flush();return 0;
}
/*
f_{i,j} 表示到i走j步是否可行,f_{i,0} = 1
f_{nxt_i,j+1} |= f_{i,j} (j >= d_{i,nxt_i})
j,1e9不好搞,考虑n,m<=150有无什么好处
矩阵乘法?猜的,尝试胡一下
考虑在d_{i,nxt_i}允许(i,nxt_i)这条边
考虑m条边排序,然后两两之间去跑矩阵快速幂
然后注意到这个只有可行性,我们连一条(n,n,0)的边
然后我们二分求解即可,复杂度m*n^3*log+n^3log^2,真是一对【】啊
考虑bitset优化,都/w,然后可以了
*/