洛谷 P2656 采蘑菇(Tarjan)

题目描述
小胖和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的模板题,也是时候做一些难一点的题了233。这道题的做法是,我们用Tarjan求一遍强连通分量,对于每一个强连通分量中的两个有路径相连的点,他们之间的路径上的蘑菇一定能够被采光,所以我们给这个缩点后的强连通分量加上一个点权。而对于连接两个强连通分量的路径我们显然只能走一次,所以我们就可以把强连通分量缩点后跑最长路,最后求一下和就是答案。
PS:这道题我从洛谷上测只有60分,然而我扒下数据来自己测能AC。我还以为是太慢了,于是加了手读和spfa的slf优化,事实证明并没有什么用QAQ
所以这里附一下数据:我是数据╮(╯▽╰)╭

代码如下

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int MAXV=100100;
const int MAXE=500100;
int first[MAXV],nxt[MAXE<<1],first2[MAXV],nxt2[MAXE<<1];
int dfn[MAXV],low[MAXV],d[MAXV],dian[MAXV];
int stack[MAXV],scc[MAXV];
int n,m,tot,tot1,tot2,snum,cnt;
bool in_stack[MAXV],used[MAXV];
struct edge
{
    int from,to,cost;
    double x;
}es[MAXE<<1];
struct edg
{
    int from,to,cost;
}ex[MAXE<<1];
void init()
{
    memset(first,-1,sizeof(first));
    memset(first2,-1,sizeof(first2));
    tot=0;
    tot2=0;
}
void build(int f,int t,int d,double c)
{
    es[++tot]=(edge){f,t,d,c};
    nxt[tot]=first[f];
    first[f]=tot;
}
void build2(int f,int t,int d)
{
    ex[++tot2]=(edg){f,t,d};
    nxt2[tot2]=first2[f];
    first2[f]=tot2;
}
void group(int u)
{
    dfn[u]=low[u]=++tot1;
    stack[++snum]=u;
    in_stack[u]=1;
    for(int i=first[u];i!=-1;i=nxt[i])
    {
        int v=es[i].to;
        if(!dfn[v])
        {
            group(v);
            low[u]=min(low[u],low[v]);
        }
        else if(in_stack[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])//找到强连通分量 
    {
        cnt++;
    //  cout<<u<<" "<<cnt<<endl;
        while(stack[snum+1]!=u)
        {
            scc[stack[snum]]=cnt;
            in_stack[stack[snum]]=0;
            snum--;
        }
    }
}
deque<int> Q;
void spfa(int s)//跑最长路 
{
    Q.push_front(s);
    used[s]=1;
    d[s]=dian[s];
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop_front();
        used[u]=0;
        for(int i=first2[u];i!=-1;i=nxt2[i])
        {
            int v=ex[i].to;
            if(d[v]<d[u]+ex[i].cost+dian[v])
            {
                d[v]=d[u]+ex[i].cost+dian[v];//加上点权 
                if(!used[v])
                {
                    used[v]=1;
                    if(Q.empty()) Q.push_back(v);//slf优化 
                    else if(d[Q.front()]<=d[v]) Q.push_back(v);
                    else Q.push_front(v);
                }
            }
        }
    }
}
int read()//手读 
{
    int num=0,flag=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-') flag=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        num=num*10+c-'0';
        c=getchar();
    }
    return num*flag;
}
int main()
{
    int s,sum=0,maxx=0;
    n=read();
    m=read();
//  printf("%d",m);
    init();
    for(int i=1;i<=m;i++)
    {
        int f,t,d;
        double c;
        f=read();
        t=read();
        d=read();
        scanf("%lf",&c);
        build(f,t,d,c);
    }
    s=read();
    for(int i=1;i<=n;i++)
        if(!dfn[i]) group(i);
    for(int i=1;i<=tot;i++)
    {
        int xx,yy;
        xx=es[i].from;
        yy=es[i].to;
        if(scc[xx]==scc[yy])//缩点 
        {
            int d=es[i].cost;
            while(d!=0)
            {
                dian[scc[xx]]+=d;//给这个点加上点权 
                d=d*es[i].x;//直到全部采走 
            }
        }
        else build2(scc[xx],scc[yy],es[i].cost);//在两个强连通分量之间建新的变 
    }
    spfa(scc[s]);//从起点属于的强连通分量开始跑最长路 
    for(int i=1;i<=cnt;i++)
        if(d[i]>maxx) maxx=d[i];//找一个最大值 
    printf("%d\n",maxx);
    return 0;
}
发布了81 篇原创文章 · 获赞 2 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/Loi_YZS/article/details/53085761