【JZOJ 4771】 爬山

58 篇文章 0 订阅
4 篇文章 0 订阅

Description

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

对于 100% 的数据, N,M≤500000。(数据有梯度,注意答案的大小)

Analysis

水题,被我秒了,因为是原题,以前做过,记得好像有更好的方法。
我的辣鸡方法是tarjan缩点然后拓扑dp,都是经典的套路。
但是在递归的时候爆栈了80分
所以呢,人工栈,就能ac了。
为什么会有原题出现啊唉

Code

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int N=500010;
int n,m,num,tot,S,to[N],next[N],last[N],co[N];
int now,top,low[N],dfn[N],sta[N],stack[N],pos[N];
ll f[N],a[N],val[N];
bool vis[N];
queue<int> q;
struct edge
{
    int u,v;
}b[N];
void link(int u,int v)
{
    to[++tot]=v,next[tot]=last[u],last[u]=tot;
}
void tarjan(int S)
{
    stack[top=1]=S;
    while(top)
    {
        int v=stack[top],st=last[v];
        if(pos[top])
        {
            st=pos[top],low[v]=min(low[v],low[to[pos[top]]]);
        }
        else
        {
            sta[++sta[0]]=v;
            low[v]=dfn[v]=++now;
            vis[v]=1;
        }
        bool p=0;
        for(int i=st;i;i=next[i])
        {
            int u=to[i];
            if(!dfn[u])
            {
                pos[top]=i;
                stack[++top]=u;
                p=1;
                break;
            }
            else
            if(vis[u]) low[v]=min(low[v],dfn[u]);
        }
        if(p) continue;
        if(low[v]==dfn[v])
        {
            num++;
            for(;sta[sta[0]+1]!=v;vis[sta[sta[0]--]]=0)
                co[sta[sta[0]]]=num,val[num]+=a[sta[sta[0]]];
        }
        pos[top--]=0;
    }
}
void dp()
{
    q.push(S);
    f[S]=val[S];
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=last[u];i;i=next[i])
        {
            int v=to[i];
            if(val[v]+f[u]>f[v])
            {
                f[v]=val[v]+f[u];
                q.push(v);
            }
        }
    }
}
int main()
{
    scanf("%d %d",&n,&m);
    fo(i,1,m)
    {
        scanf("%d %d",&b[i].u,&b[i].v);
        link(b[i].u,b[i].v);
    }
    fo(i,1,n) scanf("%lld",&a[i]);
    scanf("%d",&S);
    tarjan(S);
    memset(last,0,sizeof(last));
    tot=0;
    fo(i,1,m)
    {
        int u=b[i].u,v=b[i].v;
        u=co[u],v=co[v];
        if(u!=v) link(u,v);
    }
    S=co[S];
    dp();
    int k,x;
    ll ans=0;
    scanf("%d",&k);
    fo(i,1,k)
    {
        scanf("%d",&x);
        x=co[x];
        ans=max(ans,f[x]);
    }
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值