Ralph and Mushrooms
题解
首先,我们可以对这个图根据连通分量进行缩点,因为当进入一个连通分量后,其中的每个点都是可以被采完的。
于是,我们可以先将每条边对当前块的贡献维护出来加到块中,每个块的权值为
令,。
之后再通过拓扑序将最大的一条路径求出来即可。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000005
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x7f7f7f7f7f7f7f7f;
const int mo=1e9+7;
typedef pair<LL,LL> pii;
int n,m,head[MAXN],tot,s;
LL ww[MAXN],f[MAXN],ans;
int dfn[MAXN],low[MAXN],idx,num;
int sta[MAXN],stak,cnt,belong[MAXN];
int Head[MAXN],Tot,deg[MAXN];
bool insta[MAXN];
struct edge{int from,to,nxt;LL paid;}e[MAXN<<1],E[MAXN<<1];
void addEdge(int u,int v,LL w){e[++tot]=(edge){u,v,head[u],w};head[u]=tot;}
void addedge(int u,int v,LL w){E[++Tot]=(edge){u,v,Head[u],w};Head[u]=Tot;}
void tarjan(int u){
dfn[u]=low[u]=++idx;sta[++stak]=u;insta[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v])tarjan(v),low[u]=min(low[u],low[v]);
else if(insta[v])low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
cnt++;int v;
do{
v=sta[stak--];
belong[v]=cnt;insta[v]=0;
}while(u!=v);
}
}
LL calc(LL x){
LL tmp=sqrt(2.0*x+0.25)-0.5;
return x+tmp*x-(tmp+1LL)*(tmp+2LL)*tmp/6;
}
signed main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;LL w;
scanf("%d %d %lld",&u,&v,&w);
addEdge(u,v,w);
}
scanf("%d",&s);
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
//for(int i=1;i<=n;i++)printf("%d:%d\n",i,belong[i]);
for(int i=1;i<=m;i++){
int u=e[i].from,v=e[i].to;
if(belong[u]==belong[v])
ww[belong[u]]+=calc(e[i].paid);
}
for(int i=1;i<=m;i++){
int u=e[i].from,v=e[i].to;LL w=e[i].paid;
if(belong[u]!=belong[v])
addedge(belong[u],belong[v],w+ww[belong[v]]),
deg[belong[v]]++;
}
stak=idx=0;
for(int i=1;i<=cnt;i++){if(!deg[i])sta[++stak]=i;f[i]=-INF;}
f[belong[s]]=ww[belong[s]];
while(stak){
int u=sta[stak--];
for(int i=Head[u];i;i=E[i].nxt){
int v=E[i].to;LL w=E[i].paid;
deg[v]--;if(!deg[v])sta[++stak]=v;
f[v]=max(f[v],f[u]+w);
}
}
for(int i=1;i<=cnt;i++)ans=max(ans,f[i]);
printf("%lld",ans);
return 0;
}