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; }