【NOIP模拟】爬山

18 篇文章 0 订阅
6 篇文章 0 订阅

Description

国家一级爬山运动员h10今天获得了一张有着密密麻麻标记的地图,在好奇心的驱使下,他又踏上了去爬山的路。
对于爬山,h10有一个原则,那就是不走回头路,于是他把地图上的所有边都标记成了有向边。他决定从点S出发,每到达一个新的节点他就可以获得一定的成就值。同时h10又是一个很珍惜时间的运动员,他不希望这次爬山的成就值白白浪费,所以最后他一定要在一个存档点停下,保存自己的成就值。
请你计算出在此次爬山运动中h10能够得到的最大成就值。保证h10能走到存档点。

Solution

明明是一道大水题,结果题目的意思没有讲清楚。
题意是每条边可以走很多次,那么明显一个强连通分量可以缩成一个点了。
用tarjan缩完点之后,再用spfa找最长路就好了。

用dfs的tarjan会爆栈

所以要打非递归般的tarjan。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstring>
#include<stack>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
#define rep1(i,a) for(i=first1[a];i;i=next1[i])
using namespace std;
const int maxn=500007;
typedef long long ll;
int i,j,k,l,t,n,m,S,q;
int first[maxn],next[maxn],last[maxn],num;
int first1[maxn],next1[maxn],last1[maxn],num1,b[maxn],nn;
int data[maxn];
ll d[maxn],ans,a[maxn],zhi[maxn];
int low[maxn],dfn[maxn],dfsx;
bool bz[maxn],az[maxn];
int st[maxn],hhh,e[maxn],g[maxn];
stack<int> zh;
void add(int x,int y){
    last[++num]=y,next[num]=first[x],first[x]=num;
}
void add1(int x,int y){
    last1[++num1]=y,next1[num1]=first1[x],first1[x]=num1;
}
void tarjan(int x)
{
    int i;
    zh.push(x);
    dfn[x]=low[x]=++dfsx;
    st[++st[0]]=x;
    az[x]=1;
    while(!zh.empty())
    {
        int t=zh.top();
        rep(i,t)
        {
            if(!dfn[last[i]]){
                dfn[last[i]]=low[last[i]]=++dfsx;
                st[++st[0]]=last[i];az[last[i]]=1;
                zh.push(last[i]);
                break;
            }
        }
        if(t==zh.top())
        {
            rep(i,t)
            {
                if(dfn[last[i]]>dfn[t])low[t]=min(low[t],low[last[i]]);
                else if(az[last[i]])low[t]=min(low[t],dfn[last[i]]);
            }
            if(dfn[t]==low[t])
            {
                ++hhh;
                int j;
                do
                {
                    j=st[st[0]--];
                    az[j]=0;
                    b[j]=hhh;
                }while(j!=t);
            }
            zh.pop();
        }
    }
}
void spfa(int x){
    int head=0,tail=1,now,i,j;
    data[1]=x,d[x]=zhi[x];bz[x]=1;
    while(head<tail){
        now=data[++head];
        rep1(i,now){
            if(d[last1[i]]<d[now]+zhi[last1[i]]){
                d[last1[i]]=d[now]+zhi[last1[i]];
                if(!bz[last1[i]]){
                    bz[last1[i]]=1;
                    data[++tail]=last1[i];
                }
            }
        }
        bz[now]=0;
    }
}
int main(){
//  freopen("fan.in","r",stdin);
    scanf("%d%d",&n,&m);
    fo(i,1,m){
        scanf("%d%d",&k,&l);
        e[i]=k,g[i]=l;
        add(k,l);
    }
    fo(i,1,n)scanf("%d",&a[i]);
    fo(i,1,n){
        if(!dfn[i]){
            tarjan(i);
        }
    }
    fo(i,1,n){
        zhi[b[i]]+=a[i];
    }
    fo(i,1,m){
        if(b[e[i]]==b[g[i]])continue;
        add1(b[e[i]],b[g[i]]);    
    }
    scanf("%d%d",&S,&q);
    memset(bz,0,sizeof(bz));
    spfa(b[S]);
    fo(i,1,q){
        scanf("%d",&k);
        ans=max(ans,d[b[k]]);
    }
    printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值