[BZOJ1179] [Apio2009]Atm(强连通)

题意传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1179


由于一个环上的点一定可以全部拿到,所以可以考虑强连通缩点,缩点之后用连通块建新图,将有酒吧的连通块连到ed,跑一边最长路即可。

(初始化的位置一定要放对啊啊啊啊!!!


code:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=500010;
struct node
{
    int x,y,c,next;
}a[maxn*2],b[maxn*2];
int len1,last1[maxn];
int len2,last2[maxn];
int n,m;
void ins1(int x,int y)
{
    len1++; a[len1].x=x;a[len1].y=y;
    a[len1].next=last1[x]; last1[x]=len1;
}
void ins2(int x,int y,int c)
{
    len2++; b[len2].x=x;b[len2].y=y;b[len2].c=c;
    b[len2].next=last2[x]; last2[x]=len2;
}
int low[maxn],dfn[maxn],id;
int sta[maxn],top; bool insta[maxn];
int bl[maxn],scc;
int w[maxn],sum[maxn];
void dfs(int x)
{
    dfn[x]=low[x]=++id;
    sta[++top]=x; insta[x]=true;
    for(int k=last1[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(dfn[y]==-1)
        {
            dfs(y);
            low[x]=min(low[x],low[y]);
        }
        else 
        {
            if(insta[y]) low[x]=min(low[x],dfn[y]);
        }
    }
    if(low[x]==dfn[x])
    {
        int i; scc++;
        do{
            i=sta[top--];
            bl[i]=scc;
            insta[i]=false;
            sum[scc]+=w[i];
        }while(i!=x);
    }
}
int st,ed,stt;
bool ved[maxn];
void rebuild()
{
    st=bl[stt];
    for(int i=1;i<=len1;i++)
    {
        int x=a[i].x,y=a[i].y;
        if(bl[x]!=bl[y])
            ins2(bl[x],bl[y],sum[bl[x]]);
    }
    for(int i=1;i<=n;i++)
    {
        if(ved[i]==true)
        {
            ins2(bl[i],ed,sum[bl[i]]);
        }
    }
}
int d[maxn];
int list[maxn*2],head,tail;
bool vs[maxn];
void spfa()
{
    memset(d,-1,sizeof(d)); d[st]=0;
    memset(vs,false,sizeof(vs)); vs[st]=true;
    head=1,tail=2; list[head]=st;
    while(head!=tail)
    {
        int x=list[head];
        for(int k=last2[x];k;k=b[k].next)
        {
            int y=b[k].y;
            if(d[y]<d[x]+b[k].c)
            {
                d[y]=d[x]+b[k].c;
                if(vs[y]==false)
                {
                    vs[y]=true;
                    list[tail++]=y;
                }
            }
        }
        head++; vs[x]=false;
    }
}
void init()
{
    id=top=scc=len1=len2=0;
    memset(dfn,-1,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(sta,0,sizeof(sta));
    memset(bl,0,sizeof(bl));
    memset(sum,0,sizeof(sum));
    memset(last1,0,sizeof(last1));
    memset(last2,0,sizeof(last2));
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        ins1(u,v);
    }
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    int tt; scanf("%d%d",&stt,&tt);
    for(int ti=1;ti<=tt;ti++)
    {
        int x;scanf("%d",&x);
        ved[x]=true;
    }

    for(int i=1;i<=n;i++)
        if(dfn[i]==-1)
            dfs(i);
    ed=scc+1;

    rebuild();
    spfa();

    printf("%d\n",d[ed]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值