洛谷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真是太好不过了。刚上来我们就先进行缩点,缩完点后,重新建图,在建图的过程中处理出每个点的点权(缩出来的点如果原本是一个环,我们就不停地跑,每次都给他加上蘑菇数量*恢复系数,直到蘑菇数量为0,这样我们可以知道,这个强连通分量里面的能采到的所有蘑菇数,我们都赋给了这个新点的点权)。这样,我们再跑一边spfa,求一下最长路,这样就保证了我们采到的蘑菇尽量多。正确性显然。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<queue>
using namespace std;
const int maxn=80005;
struct dqs
{
    int f,t,c;
    double hui;
}hh[maxn<<2];
struct dqm
{
    int ff,tt,cc;
}ha[maxn<<2];
int first[maxn],next[maxn<<2];
int tot=0;
void build(int f,int t,int c,double hui)
{
    hh[++tot]=(dqs){f,t,c,hui};
    next[tot]=first[f];
    first[f]=tot;
}
int lala=0;
int fir[maxn],nxt[maxn<<2],d[maxn<<2];
bool used[maxn];
void add(int f,int t,int c)
{
    ha[++lala]=(dqm){f,t,c};
    nxt[lala]=fir[f];
    fir[f]=lala;
}
int tot1=0,snum=0,cnt=0;
int dfn[maxn],low[maxn],stack[maxn],jlqlt[maxn],size[maxn],dian[maxn];
bool in_stack[maxn]; 
void group(int x)
{
    dfn[x]=low[x]=++tot1;
    stack[++snum]=x;
    in_stack[x]=1;
    for(int i=first[x];i;i=next[i])
    {
        int u=hh[i].t;
        if(!dfn[u])
        {
            group(u);
            low[x]=min(low[x],low[u]);
        }
        else if(in_stack[u])
            low[x]=min(low[x],dfn[u]);
    }
    if(dfn[x]==low[x])
    {
        cnt++;
        while(true)
        {
            jlqlt[stack[snum]]=cnt;
            size[cnt]++;
            in_stack[stack[snum]]=0;
            snum--;
            if(stack[snum+1]==x)
            break;
        }
    }
}
queue<int>q;
void spfa(int s)
{
    d[s]=dian[s];
    q.push(s);
    used[s]=1;
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        used[x]=0;
        for(int i=fir[x];i;i=nxt[i])
        {
            int u=ha[i].tt;
            if(d[u]<d[x]+ha[i].cc)
            {
                d[u]=d[x]+ha[i].cc+dian[u];
                if(!used[u])
                {
                    used[u]=1;
                    q.push(u);
                }
            }
        }
    }
}
int n,m,s;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int f,t,c;
        double hui;
        scanf("%d%d%d%lf",&f,&t,&c,&hui);
        build(f,t,c,hui);
    }
    scanf("%d",&s);
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            group(i);
    for(int i=1;i<=tot;i++)
    {
        int xx,yy;
        xx=hh[i].f;
        yy=hh[i].t;
        if(jlqlt[xx]==jlqlt[yy])
        {
            int d=hh[i].c;
            while(d)
            {
                dian[jlqlt[xx]]+=d;
                d=d*hh[i].hui;
            }
        }
        else
            add(jlqlt[xx],jlqlt[yy],hh[i].c);
    }
    spfa(jlqlt[s]);
    int ans=0;
    for(int i=1;i<=cnt;i++)
        ans=max(ans,d[i]);
    printf("%d\n",ans);
    return 0;
}

神犇Loi_a打了拓扑排序,神的不得了啊,Orz。这里在征求他的同意下,也列出他的代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<cstring>
#define maxn 80005
using namespace std;
struct E{
    int to,nxt;
    int d;double w; 
}b[300005];
struct C{
    int f,t,d;
    double w;
}cc[200005];
int fst[maxn],tot;
void build(int f,int t,int d,double w)
{b[++tot]=(E){t,fst[f],d,w};fst[f]=tot;}
int dfs_c;
int low[maxn],dfn[maxn];
stack<int> S;
int sccn[maxn],sz[maxn];
int cnt;
void dfs(int u)
{
    dfn[u]=low[u]=++dfs_c;
    S.push(u);
    for(int i=fst[u];i;i=b[i].nxt)
    {
        int v=b[i].to;
        if(!dfn[v])
        {
            dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!sccn[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        cnt++;
        while(true)
        {
            int v=S.top();
            S.pop();
            sccn[v]=cnt;
            sz[cnt]++;
            if(v==u) break;
        }
    }
}
int n,m,st;
int val[maxn],dis[maxn];
int rd[maxn];
queue<int> q;
void top()
{
    q.push(sccn[st]);
    dis[sccn[st]]=val[sccn[st]];
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=fst[u];i;i=b[i].nxt)
        {
            int v=b[i].to;
            rd[v]--;
            dis[v]=max(dis[v],dis[u]+val[v]+b[i].d);
            if(rd[v]==0) q.push(v);
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    int s,t,v;double w;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%lf",&s,&t,&v,&w);
        cc[i]=(C){s,t,v,w};
        build(s,t,v,w);
    }
    scanf("%d",&st);
    dfs(st);
    memset(fst,0,sizeof(fst));
    tot=0;
    for(int i=1;i<=m;i++)
    {
        int f=cc[i].f,t=cc[i].t;
        if(!sccn[f]) continue;
        if(sccn[f]==sccn[t])
        {
            int d=cc[i].d;
            while(d)
            {
                val[sccn[f]]+=d;
                d*=cc[i].w;
            }
        }
        else
        {
            build(sccn[f],sccn[t],cc[i].d,cc[i].w);
            rd[sccn[t]]++;
        }
    }
    top();
    int ans=0;
    for(int i=1;i<=cnt;i++)
    {
        ans=max(ans,dis[i]);
    }
    printf("%d",ans);
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值