洛谷P3627 [APIO2009]抢掠计划

题意:一个n个点,m条边的有向图,每个点有权值(取款机),其中有一些点是酒吧,给定起点S,求从起点出发到达一个酒吧所能取到的最大权值,注意一个点一条边可以重复经过多次,但每个取款机只能取一次。

思路:1.“有向图”“重复多次”我们可以想到缩点,因为到达一个极大强连通分量时,分量中每个点都可取到

2.缩点后跑最长路,转化为负权SPFA最短路,计算出dist数组

3.最后对于每个酒吧的dist取最大值即为答案

细节:1.缩点的写法

2.重建图后注意belong数组

3.点权化边权,每条有向边(u,v)的权值为终点v的权值,因此一开始dist[belong[s]]的值不是0,而是其自身所在强连通分量的大小

code:

#include<bits/stdc++.h>
using namespace std;
const int inf=0x7fffffff;
const int maxn=500005;

queue<int>q;
int stak[maxn],top=0;
int n,m,S,P,ans=0;
int from[maxn],head[maxn],Next[maxn],to[maxn],val[maxn],tot=0;
int Head[maxn],Nxt[maxn],To[maxn],w[maxn],tot2=0;
int belong[maxn],dfn[maxn],low[maxn],size[maxn],cls=0,cnt=0;
int dist[maxn];
bool ins[maxn],vis[maxn];

void add(int x,int y){
    from[++tot]=x; to[tot]=y; Next[tot]=head[x]; head[x]=tot;
}

void add2(int x,int y,int z){
    To[++tot2]=y; Nxt[tot2]=Head[x]; Head[x]=tot2; w[tot2]=z;
}

int read(){
    int x=0,f=1; char c=getchar();
    while(c>'9'||c<'0'){
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+c-'0';
        c=getchar();
    }
    return x*f;
}

void tarjan(int x){
    stak[++top]=x; ins[x]=1;
    dfn[x]=low[x]=++cls;
    for(int i=head[x];i;i=Next[i]){
        int y=to[i];
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(ins[y]) low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x]){
        cnt++;
        int y;
        do{
            y=stak[top--];
            ins[y]=0;
            belong[y]=cnt;
            size[cnt]+=val[y];
        }while(x!=y);
    }
}

void spfa(){
    for(int i=1;i<=cnt;i++) dist[i]=inf;
    q.push(belong[S]);
    dist[belong[S]]=-size[belong[S]];
    vis[belong[S]]=1;
    while(!q.empty()){  
        int x=q.front(); q.pop();
        vis[x]=0;
        for(int i=Head[x];i;i=Nxt[i]){
            int y=To[i];
            if(dist[x]+w[i]<dist[y]){
                dist[y]=dist[x]+w[i];
                if(!vis[y]){
                    vis[y]=1;
                    q.push(y);
                }
            }
        }
    }
}

int main(){
    n=read(),m=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read();
        add(x,y);
    }
    for(int i=1;i<=n;i++) val[i]=read();
    for(int i=1;i<=n;i++){   //缩点
        if(!dfn[i]) tarjan(i);
    }
    for(int i=1;i<=m;i++){  //重建图
        if(belong[from[i]]!=belong[to[i]]){
            add2(belong[from[i]],belong[to[i]],-size[belong[to[i]]]);
        }
    }
    S=read(); P=read();
    spfa();  //最短路
    for(int i=1;i<=P;i++){
        int x=read();
        ans=max(ans,-dist[belong[x]]);
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xvzichen/p/11789095.html