这破题调了我一天...错了一大堆细节T T
首先显然可以将边权先排序,然后逐个加进图中。
加进图后,倍增跑跑看能不能到达n,不能的话加新的边继续跑。
倍增的时候要预处理出h[i]表示转移矩阵的2^0~i的或和,转移是h[i]=h[i-1]*h[i-1]。
注意两个矩阵包含0~i和0~j相乘的时候,得到的矩阵是0~i*j的,而两个矩阵包含0~i和0~j或起来的时候,得到的矩阵是j~i+j的。
倍增的时候因为必须答案单调,所以当前的值必须或上之前的值。
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<bitset> #include<algorithm> #include<cmath> using namespace std; const int maxn=160, inf=1e9+160; struct mtx{bitset<maxn>mp[maxn]; mtx(){for(int i=0;i<maxn;i++) mp[i].reset();}}tmp, tmp2, f, base, h[35]; struct poi{int x, y, dis;}a[maxn]; int n, m; mtx operator *(mtx a, mtx b) { mtx c; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a.mp[i][j]) c.mp[i]|=b.mp[j]; return c; } mtx operator |(mtx a, mtx b) { mtx c; for(int i=1;i<=n;i++) c.mp[i]|=a.mp[i]|b.mp[i]; return c; } inline void power(int b) { if(b<=0) return; for(;b;b>>=1, tmp=tmp*tmp) if(b&1) f=f*tmp; } inline bool cmp(poi a, poi b){return a.dis<b.dis;} int main() { scanf("%d%d", &n, &m); for(int i=1;i<=m;i++) scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].dis); sort(a+1, a+1+m, cmp); a[m+1].dis=inf; if(a[1].dis) return puts("Impossible"), 0; for(int i=1;i<=n;i++) f.mp[i][i]=1; for(int i=1;i<=m;i++) { tmp=base; power(a[i].dis-a[i-1].dis); base.mp[a[i].x][a[i].y]=1; int up=log2(a[i+1].dis-a[i].dis); h[0]=base; for(int j=1;j<=n;j++) h[0].mp[j][j]=h[0].mp[j][j]|1; mtx tmpx; for(int j=1;j<=n;j++) tmpx.mp[j][j]=1; if(a[i+1].dis-a[i].dis & 1) tmpx=tmpx|(tmpx*h[0]); for(int j=1;j<=up;j++) { h[j]=h[j-1]*h[j-1]; if(a[i+1].dis-a[i].dis & (1<<j)) tmpx=tmpx|(tmpx*h[j]); } tmpx=f*tmpx; if(!tmpx.mp[1][n]) continue; mtx tmp1=f; int ans=0; for(int j=up;j>=0;j--) if(a[i].dis+ans+(1<<j)<=a[i+1].dis) { tmp2=tmp1; tmp2=tmp2*h[j]; if(tmp2.mp[1][n]) continue; ans+=(1<<j); tmp1=tmp2; } return printf("%d\n", a[i].dis+ans+1), 0; } puts("Impossible"); }