[NOIP2009][LuoguP1073] 最优贸易 - Tarjan,拓扑+DP

时间:2023-03-08 16:50:02
[NOIP2009][LuoguP1073] 最优贸易 - Tarjan,拓扑+DP

Description&Data

题面:https://www.luogu.org/problemnew/show/P1073

Solution

Tarjan对联通块缩点,在DAG上按照拓扑序更新最低买入价,到每个点时再更新一下答案,即联通块内最大卖出价减去沿途的最低价格,复杂度O(n).

看机房其他人有写双向SPFA,代码短一些,反向建一张图,一遍跑最大价格,一遍跑最小价格,这样保证最大差值产生自同一条路径,最后取差即为答案。

Tarjan代码如下:(SPFA在后面)

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<stack>
#define maxn 100005
#define maxm 500005
using namespace std;
struct edge{
    int to,nxt;
}e[maxm<<1],ce[maxm];
int edgenum,lnk[maxn],a[maxn],n,m,u,v,t;
int dfn[maxn],low[maxn],blk[maxn],dgr[maxn],cnt,num;
int mn[maxn],mx[maxn],f[maxn],q[maxn],hd=0,tl=1;
int ans[maxn];
bool vis[maxn];
stack<int> st;
inline int rd()
{
    int x=0;char c=getchar();
    while(!isdigit(c))c=getchar();
    while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x;
}
void add(int bgn,int end)
{
    e[++edgenum].to=end;
    e[edgenum].nxt=lnk[bgn];
    lnk[bgn]=edgenum;
}
int cedgenum,clnk[maxn];
void c_add(int bgn,int end)
{
    ce[++cedgenum].to=end;
    ce[cedgenum].nxt=clnk[bgn];
    clnk[bgn]=cedgenum;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++cnt;
    vis[x]=1;
    st.push(x);
    for(int p=lnk[x];p;p=e[p].nxt){
        int y=e[p].to;
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(vis[y])low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x]){
        int now;
        num++;
        do{
            now=st.top();st.pop();
            mn[num]=min(mn[num],a[now]);
            mx[num]=max(mx[num],a[now]);
            vis[now]=0;
            blk[now]=num;
        }while(now!=x);
    }
}
void topo()
{
    for(int i=1;i<=num;++i){
        f[i]=mn[i];
        if(!dgr[i])q[tl++]=i;
    }

    while(++hd<tl){
        int u=q[hd];
        for(int p=clnk[u];p;p=ce[p].nxt){
            int y=ce[p].to;
            if(!--dgr[y])q[tl++]=y;
        }
    }
}
void solve()
{
    hd=0;
    while(++hd<tl){
        int u=q[hd];
        ans[u]=max(ans[u],mx[u]-f[u]);
        for(int p=clnk[u];p;p=ce[p].nxt){
            int y=ce[p].to;
            f[y]=min(f[y],f[u]);
            ans[y]=max(ans[y],ans[u]);
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        mn[i]=0x3f3f3f;
    for(int i=1;i<=n;++i)a[i]=rd();
    for(int i=1;i<=m;++i){
        u=rd(),v=rd(),t=rd();
        if(t==1)add(u,v);
        else add(u,v),add(v,u);
    }
    for(int i=1;i<=n;++i)
        if(!dfn[i])tarjan(i);
    int s=blk[1],end=blk[n];
    for(int i=1;i<=n;++i){
        for(int p=lnk[i];p;p=e[p].nxt){
            int y=e[p].to;
            if(blk[i]!=blk[y])c_add(blk[i],blk[y]),dgr[blk[y]]++;
        }
    }
    topo();
    solve();
    printf("%d\n",ans[end]);
    return 0;
} 

SPFA占坑