洛谷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范围内。

输入输出样例

输入样例#:
3 3
1 2 4 0.5
1 3 7 0.1
2 3 4 0.6
1
输出样例#:
8
因为一个边可以走多次所以直接跑spfa肯定会t,所以我们可以用tarjan缩一下点,点权即为圈内的可以得到的蘑菇总数,再跑spfa就可以了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<stack>
#include<queue>
using namespace std;

const int SZ=401000;
struct edge
{
    int f,t,d;
}es[SZ],es2[SZ];
int n,m,sss;
double k[SZ];
int l[SZ],r[SZ],size[SZ];
int tot,fst[SZ],nxt[SZ];
void build(int f,int t,int d)
{
    es[++tot]=(edge){f,t,d};
    nxt[tot]=fst[f];
    fst[f]=tot;
}
void build2(int f,int t,int d)
{
    es2[++tot]=(edge){f,t,d};
    nxt[tot]=fst[f];
    fst[f]=tot;
}
stack<int>s;
int dfn[SZ],low[SZ];
int scc[SZ],cnt,cnt2;
int mg[SZ];
int trajan(int u)
{
    dfn[u]=low[u]=++cnt;
    s.push(u);
    for(int i=fst[u];i;i=nxt[i])
    {
        int v=es[i].t;//cout<<v<<" "<<dfn[v]<<endl;
        if(!dfn[v])
        {
            low[v]=trajan(v);
            low[u]=min(low[u],low[v]);
        }
        if(!scc[v]) low[u]=min(low[u],dfn[v]);
    }
    //cout<<u<<" "<<dfn[u]<<" "<<low[u]<<endl;
    if(dfn[u]==low[u])
    {
        cnt2++;
        while(1)
        {
            int ha=s.top();s.pop();
            scc[ha]=cnt2;if(ha==u) break;
        }
    }
    return low[u];
}
queue<int>q;
bool vis[SZ];
int dis[SZ],maxn;
void spfa(int ss)
{
    vis[ss]=1;
    q.push(ss);dis[ss]=mg[ss];
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=fst[u];i;i=nxt[i])
        {
            int v=es2[i].t;
            if(dis[v]<dis[u]+mg[v]+es2[i].d)
            {
                dis[v]=dis[u]+mg[v]+es2[i].d;
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }

}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%lf",&l[i],&r[i],&size[i],&k[i]);
        build(l[i],r[i],size[i]);
    }
    scanf("%d",&sss);
    trajan(sss);
    for(int i=1;i<=m;i++)//缩点
    {
        if(scc[l[i]]==scc[r[i]])
        {
            while(size[i])
            {
                mg[scc[l[i]]]+=size[i];
                //cout<<mg[scc[l[i]]]<<endl;
                size[i]*=k[i];
            }
        }
    }
    memset(fst,0,sizeof(fst));
    memset(nxt,0,sizeof(nxt));
    tot=0;
    for(int i=1;i<=m;i++)
    {
        if(scc[l[i]]!=scc[r[i]])
        build2(scc[l[i]],scc[r[i]],size[i]);
    }
    spfa(scc[sss]);
    for(int i=1;i<=cnt2;i++) maxn=max(maxn,dis[i]);
    cout<<maxn;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值