P3008 [USACO11JAN]道路和飞机Roads and Planes

P3008 [USACO11JAN]道路和飞机Roads and Planes

Dijkstra+Tarjan

因为题目有特殊限制所以不用担心负权的问题

但是朴素的Dijkstra就算用堆优化,也显然会超时。

这是因为Dj每次扩展时,总是找到费用最小那个点进行扩展。

而本题的毒瘤数据可以在一个图(设其点数为k)后连一长串负权边。这样每次扩展的最坏复杂度O(n^k),T出天际。

但是我们又可以用到题目的特殊限制:一个图被限制成若干层,每层的最短路互相无影响

显然,我们可以用Tarjan缩点法,把图分层,把每层分离出来,单独跑一遍Dj。

然后就没了。(逃

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cctype>
using namespace std;
template <typename T> inline T min(T &a,T &b) {return a<b ?a:b;}
template <typename T> inline void read(T &x){
    char c=getchar(); x=0; bool f=1;
    while(!isdigit(c)) f= !f||c=='-' ? 0:1,c=getchar();
    while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    x= f? x:-x;
}
int n,m1,m2,s,d[25002],cnt1,hd[25002],nxt[150002],ed[25002],poi[150002],val[150002];
int dfs_clock,cnt2,_top,low[25002],dfn[25002],be[25002],st[25002]; //用于Tarjan
struct data{
    int d,u;
    bool operator < (const data &tmp) const {
        if(be[u]!=be[tmp.u]) return be[u]<be[tmp.u]; //同块内先处理
        return d>tmp.d;
    }
}; priority_queue <data> h;
inline void add(int x,int y,int v){
    nxt[ed[x]]=++cnt1; hd[x]= hd[x] ? hd[x]:cnt1;
    ed[x]=cnt1; poi[cnt1]=y; val[cnt1]=v;
}
inline void tarjan(int x){ //Tarjan模板,可右转P3387
    dfn[x]=low[x]=++dfs_clock; st[++_top]=x;
    for(int i=hd[x];i;i=nxt[i]){
        int to=poi[i];
        if(!dfn[to]) tarjan(to),low[x]=min(low[x],low[to]);
        else if(!be[to]) low[x]=min(low[x],dfn[to]);
    }
    if(low[x]==dfn[x]){ //给出每个点所处层的编号
        be[x]=++cnt2;
        while(st[_top]!=x) be[st[_top--]]=cnt2;
        --_top;
    }
}
void dijkstra(){ //裸的
    memset(d,127,sizeof(d));
    h.push((data){d[s]=0,s});
    while(!h.empty()){
        data x=h.top(); h.pop();
        if(x.d!=d[x.u]) continue;
        for(int i=hd[x.u];i;i=nxt[i])
            if(x.d+val[i]<d[poi[i]]){
                d[poi[i]]=x.d+val[i];
                h.push((data){d[poi[i]],poi[i]});
            }
    }
}
int main(){
    read(n); read(m1); read(m2); read(s); int q1,q2,q3;
    for(int i=1;i<=m1;++i) read(q1),read(q2),read(q3),add(q1,q2,q3),add(q2,q1,q3);
    for(int i=1;i<=m2;++i) read(q1),read(q2),read(q3),add(q1,q2,q3);
    tarjan(s); //tarjan缩点
    for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
    dijkstra();
    for(int i=1;i<=n;++i){
        if(d[i]==d[0]) printf("NO PATH\n");
        else printf("%d\n",d[i]);
    }return 0;
}

 

posted @ 2018-09-21 13:34  kafuuchino  阅读(207)  评论(0编辑  收藏  举报