BZOJ 1179: [Apio2009]Atm

传送门

解题思路

今天这是咋了。。写了一堆假算法给了一堆假分。我竟然刚开始傻不拉几的写了个dijkstra跑最长路,结果87分???后面被告知只有spfa能跑最长路,并且需要将点权化为边权。。做法是因为所有atm都是非负数,所以不抢白不抢,我们考虑缩点。缩完点之后spfa最长路即可。多亏GhostCai神,要不得调几天估计。。。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>

using namespace std;
const int MAXN = 500005;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

int n,m,a[MAXN],Val[MAXN];
int head[MAXN],cnt,w[MAXN];
int nxt[MAXN],to[MAXN];
int nxt_[MAXN],to_[MAXN];
int head_[MAXN],cnt_;
int dfn[MAXN],low[MAXN];
int dis[MAXN],col[MAXN],col_num;
int stk[MAXN],top,num,st,ans;
int S,P,xx[MAXN],yy[MAXN];
bool vis[MAXN],b[MAXN],Bar[MAXN];

inline void add(int bg,int ed){
    to[++cnt]=ed,nxt[cnt]=head[bg],head[bg]=cnt;
}

inline void add_(int bg,int ed,int v){
    to_[++cnt_]=ed,w[cnt_]=v,nxt_[cnt_]=head_[bg],head_[bg]=cnt_;
}

inline void tarjan(int x){
    dfn[x]=low[x]=++num;
    vis[x]=1;stk[++top]=x;
    for(register int i=head[x];i;i=nxt[i]){
        int u=to[i];
        if(!dfn[u]){
            tarjan(u);
            low[x]=min(low[x],low[u]);
        }
        else if(vis[u])
            low[x]=min(low[x],dfn[u]);
    }
    if(dfn[x]==low[x]){
        col[x]=++col_num;
        while(stk[top]!=x){
            vis[stk[top]]=0;
            col[stk[top--]]=col_num;
        }
        vis[x]=0;
        top--;
    }
}

inline void spfa(){
    memset(vis,false,sizeof(vis));
    queue<int> Q;
    Q.push(st);dis[st]=Val[st];vis[st]=1;
    while(!Q.empty()){
        int x=Q.front();Q.pop();
//      cout<<x<<endl;      
        for(register int i=head_[x];i;i=nxt_[i]){
            int u=to_[i];
            if(dis[x]+w[i]>dis[u]){
                dis[u]=dis[x]+w[i];
                if(!vis[u]){
                    vis[u]=1;
                    Q.push(u);
                }
            }
        }   vis[x]=0;
    }
}

int main(){
//  freopen("2.in","r",stdin);
    n=rd();m=rd();
    for(register int i=1;i<=m;i++){
        int x=rd(),y=rd();
        xx[i]=x;yy[i]=y;
        add(x,y);
    }
    for(register int i=1;i<=n;i++) {a[i]=rd();if(!dfn[i]) tarjan(i);} 
//  for(register int i=1;i<=n;i++) cout<<col[i]<<" ";
    S=rd();P=rd();
    for(register int i=1;i<=P;i++){
        int x=rd();
        b[x]=1;
    }
    for(register int i=1;i<=n;i++)
        Val[col[i]]+=a[i];
    for(register int i=1;i<=m;i++){
        int x=xx[i],y=yy[i];
        if(b[x]) Bar[col[x]]=1;
        if(b[y]) Bar[col[y]]=1;
        if(x==S) st=col[x];
        if(y==S) st=col[y];
        if(col[x]!=col[y]) add_(col[x],col[y],Val[col[y]]);
    }
//  for(register int i=1;i<=col_num;i++)
//      cout<<Bar[i]<<" "<<Val[i]<<endl;         
    spfa();
    for(register int i=1;i<=col_num;i++)
        if(Bar[i]) ans=max(ans,dis[i]);
    printf("%d",ans);
    return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值