[2019 CSP-S赛前集训] [CF894E] Ralph and Mushrooms

题目链接:https://www.luogu.org/problem/CF894E

题目大意
Ralph打算去蘑菇森林采蘑菇。

蘑菇森林里有n个蘑菇丛,有m条有向的路连接这些丛林(可能连向自己,也可能两个丛林之间有多条路)。经过某条路时,Ralph可以采走这条路上的全部蘑菇。然而,这是一片神奇的蘑菇森林,蘑菇被采走后会重新长出来一些。但是,第k次走过这条路后,这条路上重新长出的蘑菇会比上次少k。(举个栗子,第一次有w个蘑菇,第二次有w-1个蘑菇,第三次有w-1-2个蘑菇,以此类推……)(还有,蘑菇的数量大于0)。

那么,Ralph最多可以采到多少蘑菇呢?
输入输出样例
输入 #1

2 2
1 2 4
2 1 4
1

输出 #1

16

输入 #2

3 3
1 2 4
2 3 3
1 3 8
1

输出 #2

8

这个题目应该不难想到,一个强连通分量内的所有蘑菇都可以采光(重复走,一直绕圈把路上的所有蘑菇采掉)

那么一个强连通分量内的所有蘑菇数量是多少呢?

\({n(n+1)<=w}\) 的最大的 \({n}\) ,解二次不等式,\(n\) 就是 \(1.0*\sqrt{0.25+2a}-0.5\)

然后价值就是 \(nw−\sum^{n}_{i=1}i(i+1)/2+w\) ,也就是 \({nw−n(n+1)(n+2)/6+w}\)

所以我们可以先用Tarjan缩点求出强连通分量,把边权合并到点上,重新建图,再来搜索一下就OK了;

那么注意几个地方:1.要开long long;

2.点权与边权混合加时不要漏加了终点的点权;

3.代码可能有点难调,耐心一点,一定可以调出来的(祭本题提交9次);

附上双倍经验:P2656 采蘑菇,稍微修改一下就能过了!

#include <bits/stdc++.h>
#define N (2000000+5)
#define int long long
using namespace std;
int n,m,s;
int top,sum,deep;
int dfn[N],low[N],color[N],vis[N],st[N];
int cnt[N],in[N],wp[N],f[N];
int u[N],v[N],wei[N];
vector <int> edge[N],w[N];
void Tarjan(int u)
{
	vis[u]=1;
	dfn[u]=low[u]=++deep;
	st[++top]=u;
	for(int i=0;i<edge[u].size();i++)
	{
		int now=edge[u][i];
		if(!dfn[now])
		{
			Tarjan(now);
			low[u]=min(low[u],low[now]);
		}
		else
		{
			if(vis[now]) low[u]=min(low[u],low[now]);
		}
	}
	if(dfn[u]==low[u])
	{
		color[u]=++sum;
		vis[u]=0;
		while(st[top]!=u)
		{
			color[st[top]]=sum;
			vis[st[top--]]=0;
		}
		top--;
	}
}
int dfs(int u)
{
    if(vis[u]) return f[u];
    int res=0;
	vis[u]=1;
    for (int i=0;i<edge[u].size();i++)
	{
        int now=edge[u][i];
        res=max(res,dfs(now)+w[u][i]);
    }
    return f[u]=res+wp[u];
}
int cont(int a)
{
	int k=1.0*sqrt(0.25+2*a)-0.5;
	return a*k-k*(k+1)*(k+2)/6+a;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&u[i],&v[i],&wei[i]);
		edge[u[i]].push_back(v[i]);
	}
	for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
	for(int i=1;i<=n;i++) vector<int>().swap(edge[i]);
	for(int i=1;i<=m;i++)
	{
		if(color[u[i]]==color[v[i]])
		{
			wp[color[u[i]]]+=cont(wei[i]);
		}
		else
		{
			//printf("u=%lld v=%lld\n",color[u[i]],color[v[i]]);
			edge[color[u[i]]].push_back(color[v[i]]);
			w[color[u[i]]].push_back(wei[i]);
			in[color[v[i]]]++;
		}
	}
	scanf("%lld",&s);
	memset(vis,0,sizeof(vis));
	printf("%lld\n",dfs(color[s]));
	return 0;
posted @ 2019-10-20 18:48  Xx_queue  阅读(99)  评论(0编辑  收藏  举报