【APIO2009】抢掠计划(scc+spfa)

拿到题目首先想到:一个强连通分量里的点都可以互相走到

所以我们先缩点

缩完点后只需用spfa跑一遍最长路 最后再统计一下有哪些含有酒吧的强连通分量 取最大值

就是代码有点冗长

#include<bits/stdc++.h>
#define N 500005
#define INF 0x7fffffff
using namespace std;
struct Edge
{
    int to,next;
}edge[N];
int n,m,first[N],tot,val[N];
int s,p;
bool is_bar[N],have_bar[N];
inline void addedge(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=first[x];
    first[x]=tot;
}
int dfn[N],low[N],sign,cnt,belong[N],sum[N];
bool insta[N];
stack <int> sta;
void dfs(int now)
{
    dfn[now]=low[now]=++sign;
    sta.push(now);
    insta[now]=true;
    for(int u=first[now];u;u=edge[u].next)
    {
        int vis=edge[u].to;
        if(!dfn[vis])
        {
            dfs(vis);
            low[now]=min(low[now],low[vis]);
        }
        else if(insta[vis])    low[now]=min(low[now],dfn[vis]);
    }
    if(low[now]==dfn[now])
    {
        int temp;
        cnt++;
        do
        {
            temp=sta.top();
            if(is_bar[temp])    have_bar[cnt]=true;
            sum[cnt]+=val[temp];
            insta[temp]=false;
            belong[temp]=cnt;
            sta.pop();
        }while(temp!=now);
    }
}
struct SCC
{
    int to,next,val;
}edge_spfa[N]; 
int head[N],dis[N],ans;
inline void addedge2(int x,int y,int z)
{
    tot++;
    edge_spfa[tot].to=y;
    edge_spfa[tot].next=head[x];
    edge_spfa[tot].val=z;
    head[x]=tot;
}
queue <int> q;
bool inque[N];
void spfa(int s)
{
    q.push(s);
    inque[s]=true;
    for(int i=1;i<=cnt;i++)    dis[i]=-INF;
    dis[s]=sum[s];
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        inque[now]=false;
        for(int u=head[now];u;u=edge_spfa[u].next)
        {
            int vis=edge_spfa[u].to;
            if(dis[now]+edge_spfa[u].val>dis[vis])
            {
                dis[vis]=dis[now]+edge_spfa[u].val;
                if(!inque[vis])
                {
                    q.push(vis);
                }
            }
        }
    }
    for(int i=1;i<=cnt;i++)    
        if(have_bar[i])    ans=max(ans,dis[i]);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);cout.tie(NULL);
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        cin>>x>>y;
        addedge(x,y);
    }
    for(int i=1;i<=n;i++)    cin>>val[i];
    cin>>s>>p;
    for(int i=1;i<=p;i++)
    {
        int x;
        cin>>x;
        is_bar[x]=true;
    }
    for(int i=1;i<=n;i++)    if(!dfn[i])    dfs(i);
    tot=0;
    for(int i=1;i<=n;i++)
    {
        for(int u=first[i];u;u=edge[u].next)
        {
            int vis=edge[u].to;
            if(belong[vis]!=belong[i])        
            {
                addedge2(belong[i],belong[vis],sum[belong[vis]]);
            }
        } 
    }
    spfa(belong[s]);
    cout<<ans;
    return 0;
}

 

转载于:https://www.cnblogs.com/Patrickpwq/articles/9785673.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值