【Codeforces 576D】Flights for Regular Customers

bitset,矩阵快速幂

题目大意:

给定一张 $n$ 个点 $m$ 条边的有向图。一条边有一个出现时刻,在该时刻之前不能走,之后一直能走。

现在要从 $1$ 走到 $n$,每个时刻都必须行动。问到达 $n$ 的最早时刻或指出无解。

题解:

考虑将边按出现时间排序,计算出从 $1$ 开始,某个时刻能到达的所有点。

我们用邻接矩阵存边。则每过一个时刻,其转移就类似于当前时刻能到的点的行向量右乘上这个矩阵。这里的矩阵乘法是与或乘法,其性质和加乘是相似的。

自然地想到快速幂。对于两条边之间间隔的那段时间,我们直接快速幂计算,然后再加入新边。

每次加入边的时候,需要判断此时是否能到达终点。如果当前能到达终点,则至多 $n$ 步必定到达终点否则将重复经过一个点,显然不可能。所以新加一条边后,我们只模拟不超过 $n$ 步的情况(如果 $n$ 步里又有新的边则模拟到下一条边出现),之后的直接快速幂转移。

时间复杂度 $O(n^3m\log d)$。

由于矩阵中的信息都是 01,涉及到的都是位运算,所以使用 bitset 优化即可。

时间复杂度 $O(\frac{n^3m\log d}\omega)$。

Code:

#include<iostream>
#include<bitset>
#include<algorithm>
#include<cstdlib>
using namespace std;
inline void failed(){cout<<"Impossible\n",exit(0);}
inline void success(int n){cout<<n<<'\n',exit(0);}
const int N=152;
int n,m,tim;
struct mat{
    bitset<N>b[N];
    int r,c;
    inline bitset<N>&operator[](const int n){return b[n];}
    inline mat operator*(const mat&g)const{
        mat c;
        c.r=r,c.c=g.c;
        for(int i=1;i<=r;++i)
        for(int k=1;k<=g.r;++k)
        if(b[i].test(k))c[i]|=g.b[k];
        return c;
    }
}trans,a;
struct edge{
    int u,v,t;
    inline bool operator<(const edge&rhs)const{return t<rhs.t;}
}e[N];
void pow(mat a,int b,mat&ret){for(;b;b>>=1,a=a*a)if(b&1)ret=ret*a;}
void work(int lst){
    if(tim==lst)return;
    if(lst-tim<=n)
    while(tim<lst){
        ++tim;
        a=a*trans;
        if(a[1].test(n))success(tim);
    }else
    for(int i=1;i<=n;++i){
        ++tim;
        a=a*trans;
        if(a[1].test(n))success(tim);
    }
    if(lst!=tim)pow(trans,lst-tim,a),tim=lst;
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    trans.r=trans.c=n;
    a.r=1,a.c=n;
    a[1].set(1);
    for(int i=1;i<=m;++i)cin>>e[i].u>>e[i].v>>e[i].t;
    sort(e+1,e+m+1);
    e[m+1].t=1e9+160;
    tim=0;
    for(int i=1;i<=m+1;++i){
        work(e[i].t);
        trans[e[i].u].set(e[i].v);
    }
    failed();
}