APIO 2009 抢掠计划 强联通缩点+最长路

Description

Siruseri城中的道路都是单向的。不同的道路由路口连接。按照法律的规定,在每个路口都设立了一个Siruseri银行的ATM取款机。令人奇怪的是,Siruseri的酒吧也都设在路口,虽然并不是每个路口都设有酒吧。 Banditji计划实施Siruseri有史以来最惊天动地的ATM抢劫。他将从市中心出发,沿着单向道路行驶,抢劫所有他途径的ATM机,最终他将在一个酒吧庆祝他的胜利。 使用高超的黑客技术,他获知了每个ATM机中可以掠取的现金数额。他希望你帮助他计算从市中心出发最后到达某个酒吧时最多能抢劫的现金总数。他可以经过同一路口或道路任意多次。但只要他抢劫过某个ATM机后,该ATM机里面就不会再有钱了。 例如,假设该城中有6个路口,道路的连接情况如下图所示:

市中心在路口1,由一个入口符号→来标识,那些有酒吧的路口用双圈来表示。每个ATM机中可取的钱数标在了路口的上方。在这个例子中,Banditji能抢劫的现金总数为47,实施的抢劫路线是:1-2-4-1-2-3-5。

50%的输入保证N, M<=3000。所有的输入保证N, M<=500000。每个ATM机中可取的钱数为一个非负整数且不超过4000。输入数据保证你可以从市中心沿着Siruseri的单向的道路到达其中的至少一个酒吧。

Input

第一行包含两个整数N、M。N表示路口的个数,M表示道路条数。接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号。接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数。接下来一行包含两个整数S、P,S表示市中心的编号,也就是出发的路口。P表示酒吧数目。接下来的一行中有P个整数,表示P个有酒吧的路口的编号。

Output

输出一个整数,表示Banditji从市中心开始到某个酒吧结束所能抢劫的最多的现金总数。

Sample Input

6 7 
1 2
2 3
3 5
2 4
4 1
2 6
6 5
10
12
8
16
1
5
1 4
4 3 5 6

Sample Output

47

把一个强联通分量的点缩成一个点,这个点的权值就是分量中所有点的权值之和,问题就转化为求最长路了

#include<cstdio>
#include<string.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;
const int N = 500000+10;
#define clr(a,b) (memset(a,b,sizeof(a)))
vector<int>v[N],q[N];//q[N] 是缩点形成的邻接表
stack<int>S;
bool bar[N],used[N],instack[N];
int va[N],dfn[N],low[N],belong[N],dis[N],money[N];
int n,m,ans,index1,s,p;
void tarjan(int u)
{
    low[u]=dfn[u]=++index1;
    instack[u]=true;
    S.push(u);
    for(int i=0;i<v[u].size();i++)
    {
        int t=v[u][i];
        if(!dfn[t])
        {
            tarjan(t);
            low[u]=min(low[t],low[u]);
        }
        else if(instack[t])
            low[u]=min(low[u],dfn[t]);
    }
    if(dfn[u]==low[u])
    {
        ans++;
        int t;
        do{
            t=S.top();
            S.pop();
            belong[t]=ans;
            instack[t]=false;
        }while(t!=u);
    }
}
void spfa()
{
    int start=belong[s];
    clr(dis,0);
    clr(used,false);
    queue<int>que;
    que.push(start);
    used[start]=true;
    dis[start]=money[start];
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        for (int i=0;i<q[u].size();i++)
        {
            int t=q[u][i];
            if(dis[u]+money[t]>dis[t])
            {
                dis[t]=dis[u]+money[t];
                if(used[t]==false)
                {
                    used[t]=true;
                    que.push(t);

                }
            }
        }
        used[u]=false;
    }
    int maxn=0;
    for(int i=1;i<=n;i++)
        if(bar[i]==true&&dis[belong[i]]>maxn)
            maxn=dis[belong[i]];
    printf("%d\n",maxn);
}
void init()
{
    clr(low,0);
    clr(dfn,0);
    clr(used,false);
    clr(bar,false);
    clr(instack,false);
    clr(money,0);
    ans=index1=0;
    for(int i=0;i<=n;i++)
    {
        v[i].clear();
        q[i].clear();
    }
    while(!S.empty())
        S.pop();
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        v[a].push_back(b);
    }
    for(int i=1;i<=n;i++)
        scanf("%d",&va[i]);
    scanf("%d%d",&s,&p);
    int x;
    for(int i=1;i<=p;i++)
    {
        scanf("%d",&x);
        bar[x]=true; //标记酒吧
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
            tarjan(i);
    }
    for(int i=1;i<=n;i++)
      money[belong[i]]+=va[i];
    for(int u=1;u<=n;u++)// 遍历所有的边,给缩点建边
    {
        int size=v[u].size();
        for(int j=0;j<size;j++)
        {
            int t=v[u][j];
            if(belong[u]!=belong[t])
            {
                q[belong[u]].push_back(belong[t]);
            }

        }
    }
    spfa();
    return 0;
}







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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值