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();
}