Codeforces 576D. Flights for Regular Customers 题解

题目链接:D. Flights for Regular Customers

题目大意:给定一个\(n\)个点\(m\)条边的有向图,第\(i\)条边只有再你之前经过了\(d_i\)条边之后才可以通过,求从\(1\)点到\(n\)号点的最短距离。


题解:因为边的\(d_i\)限制很麻烦,所以先考虑除掉这个限制,可以将所有边按照\(d_i\)排序后依次加入到原图中去,这样就去掉了这个限制。

那么处理答案可以先找出所有用\(d_i\)步可以到达的点,然后对整张图跑一边 bfs 求出最小的答案。

那么怎么找出\(d_i\)步可以到达的点的个数呢?可以将边集用邻接矩阵来处理,然后跑一遍矩阵快速幂即可。好了,这一题做完了。

算一下时间复杂度是\(O(n^3m\text{log}d)\)的,这个数据范围虽然小,不过应该过不掉(如果卡常过掉的请受我一拜),可是这个算法怎么优化呢?似乎没有办法优化了,这是我们想起一句极为经典话:“智商不够,压位来凑。”注意到转移矩阵只有\(01\)两个值,所以可以用 bitset 来优化,那么邻接矩阵就需要反过来,即原来\(u-->v\)的边要反向变为\(v-->u\)的边,否则再快速幂时很难优化。时间复杂度为\(O(\frac{n^3m\text{log}d}{\omega})\)

下面是代码:

#include <queue>
#include <bitset>
#include <cstdio>
#include <algorithm>
using namespace std;
const int Maxn=150;
const int Maxm=150;
const int Inf=0x3f3f3f3f;
int n,m;
struct Edge{
    int u,v;
    int d;
    friend bool operator <(Edge p,Edge q){
        return p.d<q.d;
    }
}edge[Maxm+5];
int dis[Maxn+5];
struct Matrix{
    bitset<Maxn+5> a[Maxn+5];
    friend bitset<Maxn+5> operator *(bitset<Maxn+5> a,Matrix b){
        bitset<Maxn+5> ans;
        for(int i=0;i<n;i++){
            ans[i]=(a&b.a[i]).any();
        }
        return ans;
    }
    friend Matrix operator *(Matrix a,Matrix b){
        Matrix ans;
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(a.a[i][j]){
                    ans.a[i]=ans.a[i]|b.a[j];
                }
            }
        }
        return ans;
    }
}a;
void quick_power(Matrix a,int b,bitset<Maxn+5> &ans){
    while(b){
        if(b&1){
            ans=ans*a;
        }
        a=a*a;
        b>>=1;
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].d);
        edge[i].u--;
        edge[i].v--;
    }
    sort(edge+1,edge+1+m);
    int last=0;
    bitset<Maxn+5> vis;
    vis[0]=1;
    int ans=Inf;
    for(int i=1;i<=m;i++){
        if(edge[i].d>=ans){
            break;
        }
        quick_power(a,edge[i].d-last,vis);
        last=edge[i].d;
        queue<int> q;
        a.a[edge[i].v][edge[i].u]=1;
        for(int j=0;j<n;j++){
            if(vis[j]){
                dis[j]=0;
                q.push(j);
            }
            else{
                dis[j]=Inf;
            }
        }
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(int v=0;v<n;v++){
                if(a.a[v][u]){
                    if(dis[v]==Inf){
                        dis[v]=dis[u]+1;
                        q.push(v);
                    }
                }
            }
        }
        ans=min(ans,dis[n-1]+edge[i].d);
    }
    if(ans==Inf){
        puts("Impossible");
    }
    else{
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/withhope/p/12431445.html