洛谷 P2656 采蘑菇

洛谷 P2656 采蘑菇

题目描述

小胖和ZYR要去ESQMS森林采蘑菇。

ESQMS森林间有N个小树丛,M条小径,每条小径都是单向的,连接两个小树丛,上面都有一定数量的蘑菇。小胖和ZYR经过某条小径一次,可以采走这条路上所有的蘑菇。由于ESQMS森林是一片神奇的沃土,所以一条路上的蘑菇被采过后,又会长出一些新的蘑菇,数量为原来蘑菇的数量乘上这条路的“恢复系数”,再下取整。

比如,一条路上有4个蘑菇,这条路的“恢复系数”为0.7,则第一~四次经过这条路径所能采到的蘑菇数量分别为4,2,1,0.

现在,小胖和ZYR从S号小树丛出发,求他们最多能采到多少蘑菇。

对于30%的数据,N<=7,M<=15

另有30%的数据,满足所有“恢复系数”为0

对于100%的数据,N<=80,000,M<=200,000,0.1<=恢复系数<=0.8且仅有一位小数,1<=S<=N.

输入输出格式

输入格式:
第一行,N和M

第2……M+1行,每行4个数字,分别表示一条小路的起点,终点,初始蘑菇数,恢复系数。

第M+2行,一个数字S

输出格式:
一个数字,表示最多能采到多少蘑菇,在int32范围内。

输入输出样例

输入样例#1:
3 3
1 2 4 0.5
1 3 7 0.1
2 3 4 0.6
1
输出样例#1:
8

思路:用tarjan缩点求强连通分量再重建图,spfa跑最长路。

题解:

#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
using namespace std;
const int maxn=400000+10;
struct cc{
    int from,to,cost;
    double xs;
}es[maxn],es1[maxn];
int first[maxn],nxt[maxn];
int first1[maxn],nxt1[maxn];
int tot=0;
void build(int ff,int tt,int pp,double dd)
{
    es[++tot]=(cc){ff,tt,pp,dd};
    nxt[tot]=first[ff];
    first[ff]=tot;
}
int tot1=0;
void build1(int ff,int tt,int pp)
{
    es1[++tot1]=(cc){ff,tt,pp};
    nxt1[tot1]=first1[ff];
    first1[ff]=tot1;
}
int low[maxn],dfn[maxn];
stack<int>s;
int scc_num[maxn];
int cnt=0,scc_cnt=0;
int dfs(int u)//tarjan 
{
    low[u]=dfn[u]=++cnt;
    s.push(u);
    for(int i=first[u];i;i=nxt[i])
    {
        int v=es[i].to;
        if(!dfn[v])
        {
            low[v]=dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!scc_num[v])
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u])
    {
        scc_cnt++;
        while(!s.empty())
        {
            int v=s.top(); s.pop();
            scc_num[v]=scc_cnt;
            if(u==v)
            {
                break;
            }
        }
    }
    return low[u];
}
int d[maxn];//存强连通分量的点权 
bool vis[maxn];
queue<int>q;
void pre(int u)
{
    for(int i=first[u];i;i=nxt[i])
    {
        int v=es[i].to;
        if(scc_num[u]==scc_num[v])//计算 
        {
            while(es[i].cost)
            {
                d[scc_num[u]]+=es[i].cost;
                es[i].cost*=es[i].xs;

            }
        }
        else//建边 
        {
            build1(scc_num[u],scc_num[v],es[i].cost);
        }
    }
}
int dis[maxn];
void spfa(int s)
{
    q.push(s);
    vis[s]=1;
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        vis[u]=0;
        for(int i=first1[u];i;i=nxt1[i])
        {
            int v=es1[i].to;
            if(dis[v]<dis[u]+es1[i].cost+d[v])
            {
                dis[v]=d[v]+dis[u]+es1[i].cost;
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        double t;
        scanf("%d%d%d%lf",&x,&y,&z,&t);
        build(x,y,z,t);
    }
    int s;
    scanf("%d",&s);
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
        {
            dfs(i);//tarjan缩点 
        }
    }
    for(int i=1;i<=n;i++)
    {
        pre(i);//算出每个强连通分量里可以采的蘑菇数量并重建边 
    }
    for(int i=1;i<=scc_cnt;i++)
    {
        dis[i]=d[i];
    }
    spfa(scc_num[s]);
    int ans=0;
    for(int i=1;i<=scc_cnt;i++)
    {
        ans=max(ans,dis[i]);
    }
    printf("%d",ans);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值