[JZOJ4771] 爬山

Summary

给出一个有向图,一个出发点和多个终点,每个点有一定的价值,经过该点就可以获得该价值(仅一次),问从出发点出发到任意一个终点的最大价值和(每条边可以走多次)

Solution

十分显然的想法,如果有一个环,那么这个环上所有点都可以走,所以用 Tarjan 强连通分量缩点,然后直接在 DAG Dp 就可以了

Code

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define N 500505
using namespace std;
int dt[2*N],nt[2*N],dfn[2*N],pt[2*N],low[2*N],fs[2*N],lt[2*N],n,n1,dep,m,m1,st,ed[N],rd[2*N],sk[2*N],num;
int dt1[2*N],nt1[2*N],lt1[2*N],fs1[2*N],rst[2*N][3],deep;
long long f[2*N],pr[2*N];
bool bz[2*N],b[2*N],b1[2*N];
void putin(int p,int x,int y)
{
    dt[p]=y;
    if (fs[x]==0) fs[x]=p;
    lt[x]=nt[lt[x]]=p;
}
void putin1(int p,int x,int y)
{
    dt1[p]=y;
    if (fs1[x]==0) fs1[x]=p;
    lt1[x]=nt1[lt1[x]]=p;
}
void del()
{
    if(b1[sk[sk[0]]]==1) b1[n1]=1;
    pt[sk[sk[0]]]=n1;
    pr[n1]+=pr[sk[sk[0]]];
    bz[sk[sk[0]]]=0;
    sk[sk[0]--]=0;
}
void tarjan()
{
    int i,k;
    bool tn=0;
    do
    { 
        if(n1==101)
        {
            n++;
            n--;
        }
        k=rst[deep][0];
        if(dfn[k]!=0&&dfn[dt[i]]==0) low[k]=min(low[k],low[dt[rst[deep][1]]]);
        else sk[++sk[0]]=k,dfn[k]=low[k]=++dep;
        if (rst[deep][1]==0) i=rst[deep][1]=fs[k];
        else i=rst[deep][1]=nt[rst[deep][1]];
        while(i>0)
        {
            if (dfn[dt[i]]==0) 
            {
                bz[dt[i]]=1;
                rst[++deep][0]=dt[i];
                tn=1;
                break;
            }
            else 
            {
                if (bz[dt[i]]==1) low[k]=min(low[k],dfn[dt[i]]);
                i=rst[deep][1]=nt[i]; 
            }
        }
        if (tn) 
        {
            tn=0;
            continue;
        }
        if (low[k]==dfn[k])
        {
            n1++;
            while (sk[sk[0]]!=k) del();
            del();
        }
        rst[deep][1]=rst[deep][0]=0; 
        deep--;
    }
    while(deep>0);
}
long long dp()
{
    dt[0]=0;
    int i,j;
    fo(i,n+1,n1) 
    {
        if (b[i]) continue;
        if (rd[i]==0) dt[++dt[0]]=i;
    }
    fo(i,1,dt[0])
    {   
        for(j=fs1[dt[i]];j>0;j=nt1[j])
        {
            int p=dt1[j];
            rd[p]--;
            if (rd[p]==0) dt[++dt[0]]=p;
        }
    }
    int q;
    q=1;
    while (dt[q]!=pt[st]) q++;
    memset(f,254,sizeof(f));
    f[dt[q]]=pr[dt[q]];
    long long ans=0;
    fo(i,q,dt[0])
    {
        if (b1[dt[i]]) ans=max(ans,f[dt[i]]);
        for(j=fs1[dt[i]];j>0;j=nt1[j])
        {
            int p=dt1[j];
            f[p]=max(f[p],f[dt[i]]+pr[p]);
        }
    }
    return ans;
}
int main()
{
    freopen("climb.in","r",stdin);
    freopen("climb.out","w",stdout);
    cin>>n>>m;
    int i,j;
    fo(i,1,m) 
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if (x!=y)
        putin(i,x,y);
    }
    fo(i,1,n) scanf("%lld",&pr[i]);
    cin>>st>>num;
    fo(i,1,num) scanf("%d",&ed[i]),b1[ed[i]]=1;
    dep=0;
    n1=n;
    fo(i,1,n)
    {
        deep=1;
        rst[1][0]=i;
        if (dfn[i]==0)
        bz[i]=1,tarjan();
    }
    m1=0;
    fo(i,1,n)
    {
        b[i]=0;
        if (pt[i]>0) b[i]=1;
        for(j=fs[i];j>0;j=nt[j])
        {
            int p=dt[j];
            if (pt[p]!=pt[i]) putin1(++m1,pt[i],pt[p]),rd[pt[p]]++;
        } 
    }
    memset(dt,0,sizeof(dt));
    cout<<dp();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值