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

传送门

肯定是最短路

而且题目有限制,不存在负环

所以可以跑堆优化的Dijkstra

但是因为有负边权,所以跑得很慢

这时可以利用题目的条件

每个联通块内是没有负边权的

所以可以优先处理单个块之内的最短路

这样一个块一个块地处理

可以跑得很快

可以用Tarjan处理每个点所在的联通块

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}

const int N=1e6+7;
int n,r,p,sta;
int fir[25007],from[N],to[N],val[N],cnt;//存边
inline void add(int &a,int &b,int &c)
{
    from[++cnt]=fir[a];
    fir[a]=cnt; to[cnt]=b; val[cnt]=c;
}
//int fa[25007];
//inline int find(int &x){ return x==fa[x] ? x : fa[x]=find(fa[x]); }

//Tarjan模板
int dfs_clock,cnt2,_top,low[25002],dfn[25002],be[25002],st[25002];
inline void Tarjan(int x)
{
    dfn[x]=low[x]=++dfs_clock; st[++_top]=x;
    for(int i=fir[x];i;i=from[i])
    {
        int v=to[i];
        if(!dfn[v]) Tarjan(v),low[x]=min(low[x],low[v]);
        else if(!be[v]) low[x]=min(low[x],dfn[v]);
    }
    if(low[x]==dfn[x])
    {
        be[x]=++cnt2;
        while(st[_top]!=x) be[st[_top--]]=cnt2;
        --_top;
    }
}

int dis[25007];
struct node
{
    int pos,v;
    bool operator < (const node &b) const{
        return fa[pos]!=fa[b.pos] ? fa[pos]<fa[b.pos] : v>b.v ;//优先处理同一个块内的点
    }
};
priority_queue <node> q;
void Dijk()
{
    int pos,v;
    memset(dis,0x7f,sizeof(dis));
    q.push((node){sta,0}); dis[sta]=0;
    while(!q.empty())
    {
        node u=q.top(); q.pop();
        if(u.v!=dis[u.pos]) continue;
        for(int i=fir[u.pos];i;i=from[i])
        {
            pos=to[i],v=u.v+val[i];
            if(dis[pos]>v)
            {
                dis[pos]=v;
                q.push((node){pos,v});
            }
        }
    }
}

int main()
{
    int a,b,c,xa,xb;
    cin>>n>>r>>p>>sta;
    //for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=r;i++)
    {
        a=read(); b=read(); c=read();
        add(a,b,c); add(b,a,c);
        //xa=find(a); xb=find(b);
        //if(xa!=xb) fa[xa]=xb;
    }
    //for(int i=1;i<=n;i++) find(i);

    for(int i=1;i<=p;i++)
    {
        a=read(); b=read(); c=read();
        add(a,b,c);
    }

    for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
    Dijk();

    for(int i=1;i<=n;i++)
    {
        if(dis[i]==dis[0]) printf("NO PATH\n");
        else printf("%d\n",dis[i]);
    }
    return 0;
}

后话:

这题一开始我处理同一块用的是并查集

但是不知道为什么就是没用,根本不能加速

总是T两个点

然后心态爆炸

直接把CRK大佬的Tarjan模板复制过来了

然后把处理联通块的步骤放到读入飞机航线之前(感觉没毛病吧,反正航线又不会形成强联通分量)

速度跟并查集一样...

把Tarjan放到读入航线后就过了....

总觉得数据有毒....

posted @ 2018-09-25 13:15  LLTYYC  阅读(247)  评论(0编辑  收藏  举报