[CF894E]Ralph and Mushrooms

Ralph and Mushrooms

题解

首先,我们可以对这个图根据连通分量进行缩点,因为当进入一个连通分量后,其中的每个点都是可以被采完的。

于是,我们可以先将每条边对当前块的贡献维护出来加到块中,每个块的权值为

t= \left \lfloor \sqrt{2x+\frac{1}{4}}-\frac{1}{2} \right \rfloorcalc(x)= (t+1)x-\frac{t(t+1)(2t+1)}{6}

之后再通过拓扑序将最大的一条路径求出来即可。

源码

#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;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值