【JZOJ4771】爬山

40 篇文章 0 订阅
5 篇文章 0 订阅

Description

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

Data Constraint

对于 30% 的数据, N,M≤1000,并且地图为有向无环图。
对于 100% 的数据, N,M≤500000。(数据有梯度,注意答案的大小)

Solution

30%的数据随便一个水dp就过了,因为是有向无环图。

那么100%的数据可能有环,怎么办呢?

我们发现,每个环里的点一定是可以全部取到的。

那么Tarjan缩点重连边,像30%数据那样dp就可以了。

注意,这题N很大,windows编译下递归会爆栈,那就打个人工栈吧(说的轻巧)。

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 500001
#define M 1000001
#define ll long long
using namespace std;
int dfn[N],low[N];
int dep=0,top=0;
int st[N];
int to[M],next[M],last[M],num=0;
int g[M],nx[M],ls[M],cnt=0;
int c[N],tot=0;
bool bz[N];
ll a[N],b[N];
void link1(int x,int y)
{
    cnt++;
    g[cnt]=y;
    nx[cnt]=ls[x];
    ls[x]=cnt;
}
void link(int x,int y)
{
    num++;
    to[num]=y;
    next[num]=last[x];
    last[x]=num;
}
int cur[N];
bool vis[N];
int fa[N];
int sta[N],top1=0;
void tarjan(int s)
{
    st[top=1]=s;
    while(top)
    {
        int x=st[top];
        if(!dfn[x])
        {
            low[x]=dfn[x]=++dep;
            sta[++top1]=x;
            bz[x]=true;
        }
        int p,q;
        int i;
        for(i=last[x];i;i=next[i])
        {
            int v=to[i];
            if(!dfn[v])
            {
                //tarjan(v);
                fa[v]=x;
                break;
            }
            else if(bz[v]) low[x]=min(low[x],dfn[v]);
        }
        if(!i && top>1) low[st[top-1]]=min(low[st[top-1]],low[x]);
        if(!i)
        {
            if(low[x]==dfn[x])
            {
                tot++;
                while(sta[top1+1]!=x && top1)
                {
                    c[sta[top1]]=tot;
                    b[tot]+=a[sta[top1]];
                    bz[sta[top1]]=false;
                    top1--;
                }
            }
            top--;
        }
        else st[++top]=to[i];
    }
}
ll f[N];
int d[N];
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    fo(i,1,m)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        link(x,y);
    }
    fo(i,1,n) scanf("%d",&a[i]);
    int s,p;
    cin>>s>>p;
    tarjan(s);
    fo(i,1,n)
    for(int j=last[i];j;j=next[j])
    {
        int v=to[j];
        if(c[v]!=c[i]) link1(c[i],c[v]);
    }
    int l=0,r=1;
    d[1]=c[s];
    f[c[s]]=b[c[s]];
    while(l<r)
    {
        l++;
        int x=d[l];
        for(int i=ls[x];i;i=nx[i])
        {
            int v=g[i];
            f[v]=max(f[v],f[x]+b[v]);
            d[++r]=v;
        }
    }
    ll ans=0;
    fo(i,1,p)
    {
        int x;
        scanf("%d",&x);
        ans=max(ans,f[c[x]]);
    }
    cout<<ans;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值