noip2009最优贸易(水晶球)

题目:http://codevs.cn/problem/1173/  https://www.luogu.org/problemnew/show/P1073

本来考虑缩点什么的,后来发现不用。

只要记录一下从起点走到现在经过的最小值和从终点走到现在经过的最大值就行了。

从终点走到现在就是记录一个反向边。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
using namespace std;
const int N=1e5+5,M=5e5+5;
int n,m,head1[N],xnt1,head2[N],xnt2;
ll c[N],fn[N],fx[N],mx;
bool vis1[N],vis2[N];
struct Edge{
    int next,to;
    Edge(int n=0,int t=0):next(n),to(t) {}
}edge1[M<<1],edge2[M<<1];
void add(int x,int y)
{
    edge1[++xnt1]=Edge(head1[x],y);head1[x]=xnt1;
    edge2[++xnt2]=Edge(head2[y],x);head2[y]=xnt2;
}
void read()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%lld",&c[i]);
    int x,y,z;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y);if(z==2)add(y,x);
    }
}
void dj1()
{
    memset(fn,1,sizeof fn);
    priority_queue<pair<ll,int> > q;fn[1]=c[1];q.push(make_pair(fn[1],1));
    while(q.size())
    {
        int k=q.top().second;q.pop();
        while(vis1[k]&&q.size())k=q.top().second,q.pop();
        if(vis1[k])break;vis1[k]=1;
        for(int i=head1[k],v;i;i=edge1[i].next)
            if(min(fn[k],c[v=edge1[i].to])<fn[v])
            {
                fn[v]=min(fn[k],c[v]);q.push(make_pair(fn[v],v));
            }
    }
}
void dj2()
{
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > >q;
    fx[n]=c[n];q.push(make_pair(fx[n],n));
    while(q.size())
    {
        int k=q.top().second;q.pop();
        while(vis2[k]&&q.size())k=q.top().second,q.pop();
        if(vis2[k])break;vis2[k]=1;
        for(int i=head2[k],v;i;i=edge2[i].next)
            if(max(fx[k],c[v=edge2[i].to])>fx[v])
            {
                fx[v]=max(fx[k],c[v]);q.push(make_pair(fx[v],v));
            }
    }
}
int main()
{
    read();
    dj1();dj2();
    for(int i=1;i<=n;i++)
        if(vis1[i]&&vis2[i])
            mx=max(mx,fx[i]-fn[i]);
    printf("%lld",mx);
    return 0;
}

 

posted on 2018-04-24 17:54  Narh  阅读(112)  评论(0编辑  收藏  举报

导航