[APIO2009]抢掠计划(tarjan+spfa)

来源:互联网 发布:mysql高效编程 编辑:程序博客网 时间:2024/05/23 17:55

题目描述

Siruseri 城中的道路都是单向的。不同的道路由路口连接。按照法律的规定, 在每个路口都设立了一个 Siruseri 银行的 ATM 取款机。令人奇怪的是,Siruseri 的酒吧也都设在路口,虽然并不是每个路口都设有酒吧。

Banditji 计划实施 Siruseri 有史以来最惊天动地的 ATM 抢劫。他将从市中心 出发,沿着单向道路行驶,抢劫所有他途径的 ATM 机,最终他将在一个酒吧庆 祝他的胜利。

使用高超的黑客技术,他获知了每个 ATM 机中可以掠取的现金数额。他希 望你帮助他计算从市中心出发最后到达某个酒吧时最多能抢劫的现金总数。他可 以经过同一路口或道路任意多次。但只要他抢劫过某个 ATM 机后,该 ATM 机 里面就不会再有钱了。 例如,假设该城中有 6 个路口,道路的连接情况如下图所示:

这里写图片描述

市中心在路口 1,由一个入口符号→来标识,那些有酒吧的路口用双圈来表

示。每个 ATM 机中可取的钱数标在了路口的上方。在这个例子中,Banditji 能抢 劫的现金总数为 47,实施的抢劫路线是:1-2-4-1-2-3-5。

输入输出格式

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

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

输入输出样例:

输入样例#1:

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

输出样例#1:

47

思路:考虑有环存在自然要先tarjan缩点,然后spfa跑最长路就可以了。

题解:

#include<iostream>#include<cstdio>#include<stack>#include<cstring>#include<queue>using namespace std;const int maxn=500000+10;struct cc{    int from,to;}es[maxn],es2[maxn];int first[maxn],nxt[maxn];int first2[maxn],nxt2[maxn];int tot=0;void build(int ff,int tt){    es[++tot]=(cc){ff,tt};    nxt[tot]=first[ff];    first[ff]=tot;}int num[maxn];bool f[maxn];stack<int>s;int cnt=0,scc_cnt=0;int scc_num[maxn];int low[maxn],dfn[maxn];int dfs(int u){    s.push(u);    low[u]=dfn[u]=++cnt;    for(int i=first[u];i;i=nxt[i])    {        int v=es[i].to;        if(!dfn[v])        {            low[v]=dfs(v);            low[u]=min(low[u],low[v]);        }        else if(!scc_num[v])        {            low[u]=min(low[u],low[v]);        }    }    if(low[u]==dfn[u])    {        scc_cnt++;        while(!s.empty())        {            int v=s.top(); s.pop();            scc_num[v]=scc_cnt;            if(u==v)            {                break;            }        }    }    return low[u];}void build2(int ff,int tt){    es2[++tot]=(cc){ff,tt};    nxt2[tot]=first2[ff];    first2[ff]=tot;}int sum[maxn];bool vis[maxn];int dis[maxn];queue<int>q;int ans=0;void spfa(int s){    q.push(s);    vis[s]=1;    dis[s]+=sum[s];    while(!q.empty())    {        int u=q.front(); q.pop();        vis[u]=0;        for(int i=first2[u];i;i=nxt2[i])        {            int v=es2[i].to;            if(dis[v]<dis[u]+sum[v])            {                dis[v]=dis[u]+sum[v];                if(f[v])                {                    ans=max(ans,dis[v]);                }                if(!vis[v])                {                    q.push(v);                    vis[v]=1;                }            }        }    }}int main(){    int n,m;    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++)    {        int x,y;        scanf("%d%d",&x,&y);        build(x,y);    }    for(int i=1;i<=n;i++)    {        scanf("%d",&num[i]);    }    int s,p;    scanf("%d%d",&s,&p);    dfs(s);    for(int i=1;i<=p;i++)    {        int x;        scanf("%d",&x);        f[scc_num[x]]=1;    }    tot=0;    for(int i=1;i<=n;i++)    {        if(!vis[i])        {            vis[i]=1;            sum[scc_num[i]]+=num[i];        }        for(int j=first[i];j;j=nxt[j])        {            int v=es[j].to;            if(scc_num[i]!=scc_num[v])            {                build2(scc_num[i],scc_num[v]);            }            else            {                if(!vis[v])                {                    vis[v]=1;                    sum[scc_num[v]]+=num[v];                }            }        }    }       memset(vis,0,sizeof(vis));    spfa(scc_num[s]);    printf("%d",ans);    return 0;}