• [CF894E] Ralph and Mushrooms


    问题描述

    Ralph打算去蘑菇森林采蘑菇。

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

    那么,Ralph最多可以采到多少蘑菇呢?

    输入格式

    第一行两个数n,m。

    第2~m+1行,每行三个数u,v,w,表示u到v有一条道路,上面有w个蘑菇。

    最后一行一个数s,表示起点。

    输出格式

    一个数,表示最多能采多少蘑菇。

    样例输入

    2 2
    1 2 4
    2 1 4
    1

    样例输出

    16

    解析

    既然边可以经过任意次数,那么可以想到,一个强联通分量中的所有边上的蘑菇我们一定可以采到小于等于0为止(绕着这个环走即可)。所以首先Tarjan缩点,对于不在强联通分量中的点,只能经过一次。接下来的问题就是如何计算一个环上的最大收益。

    计算环的收益可以转化为计算每一条边的收益。设边的权值为w,经过的次数k一定是满足((k+1)*k/2<=w)的最大的k,可以用二分求解。那么,一条边的收益为

    [w+(w-1)+(w-1-2)+...+(w-1-2-...-k) ]

    也就是

    [(k+1)*w-sum_{i=1}^{k}i*(i+1)/2 ]

    前缀和处理即可。在缩点后的DAG上动态规划得到最终答案。

    代码

    #include <iostream>
    #include <cstdio>
    #define int long long
    #define N 1000002
    using namespace std;
    int head[N],ver[N],nxt[N],edge[N],l;
    int head1[N],ver1[N],nxt1[N],edge1[N],l1;
    int n,m,S,i,j,tim,dfn[N],low[N],top,s[N],cnt,scc[N],w[N],sum[N],f[N];
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    void insert(int x,int y,int z)
    {
    	l++;
    	ver[l]=y;
    	edge[l]=z;
    	nxt[l]=head[x];
    	head[x]=l;
    }
    void insert1(int x,int y,int z)
    {
    	l1++;
    	ver1[l1]=y;
    	edge1[l1]=z;
    	nxt1[l1]=head1[x];
    	head1[x]=l1;
    }
    void Tarjan(int x)
    {
    	dfn[x]=low[x]=++tim;
    	s[++top]=x;
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(!dfn[y]){
    			Tarjan(y);
    			low[x]=min(low[x],low[y]);
    		}
    		else if(!scc[y]) low[x]=min(low[x],dfn[y]);
    	}
    	if(dfn[x]==low[x]){
    		cnt++;
    		while(1){
    			int y=s[top--];
    			scc[y]=cnt;
    			if(x==y) break;
    		}
    	}
    }
    int cal(int x)
    {
    	int l=0,r=100000,mid,k;
    	while(l<=r){
    		mid=(l+r)/2;
    		if(mid*(mid+1)/2<=x){
    			k=mid;
    			l=mid+1;
    		}
    		else r=mid-1;
    	}
    	return (k+1)*x-sum[k];
    }
    int dfs(int x)
    {
    	if(f[x]) return f[x];
    	f[x]=w[x];
    	for(int i=head1[x];i;i=nxt1[i]){
    		int y=ver1[i];
    		f[x]=max(f[x],w[x]+dfs(y)+edge1[i]);
    	}
    	return f[x];
    }
    signed main()
    {
    	for(i=1,j=1;i<=100000;i++,j+=i) sum[i]=sum[i-1]+j;
    	n=read();m=read();
    	for(i=1;i<=m;i++){
    		int u=read(),v=read(),w=read();
    		insert(u,v,w);
    	}
    	S=read();
    	for(i=1;i<=n;i++){
    		if(!dfn[i]) Tarjan(i);
    	}
    	for(i=1;i<=n;i++){
    		for(j=head[i];j;j=nxt[j]){
    			if(scc[i]==scc[ver[j]]) w[scc[i]]+=cal(edge[j]);
    			else insert1(scc[i],scc[ver[j]],edge[j]);
    		}
    	}
    	cout<<dfs(scc[S])<<endl;
    	return 0;
    }
    
  • 相关阅读:
    Mysql存储过程详解
    自动化测试——人人都可自制“呼死你”
    Apktool(1)——Apktool的安装
    Apktool(2)——使用前必须知道的apk知识
    写博是种心情
    webpack使用tree shaking的问题。及关于UglifyJs不支持ES6的解决方案。
    angular2 笔记
    angular2 content projection
    angular2aotwebpack 生产环境下编译angular2
    ionic2配置问题集
  • 原文地址:https://www.cnblogs.com/LSlzf/p/11674918.html
Copyright © 2020-2023  润新知