前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Luogu P2656 采蘑菇 题解

Luogu P2656 采蘑菇 题解

作者头像
yzxoi
发布2022-09-19 12:55:48
4470
发布2022-09-19 12:55:48
举报
文章被收录于专栏:OI

Luogu P2656 采蘑菇 题解

Describe

题目链接

在一个 N 个点, M 条边的有向图中,每条路可以走无数次,边权为 w_i ,边的恢复系数为 p_i 第二次走时,边权变为 w_i \times {p_i} ,第三次走时,边权变为 w_i \times {p_i} ^ 2…第 k 次走时,边权变为 w_i \times {p_i}^{k-1}(全部向下取整,直到 0 为止) 问,从 S 出发,最大的路径经过的边权和为多少?(可以重复走) 对于所有数据,N \leq 80,000 , M \leq 200,000 , 0.1\leq p_i \leq 0.8\text{且仅有一位小数},1\leq S \leq N

Solution

显然,只要我们找到一个环,那么我们就可以不停地走这个环,直到环内所有的边权为0,所以,只要找环,缩点再遍历一次就好了。

预处理

预处理出每条边的无限次走后的边权。

找环&缩点

看了下Luogu题解区内的都是Tarjan找环、缩点,那么对于我这种不会Tarjan的怎么办呢?当然是用kosaraju啦~ 什么是kosaraju?可以参考我的这篇博客(此文写的时间久远,写的不好勿喷)。

构图

找环&缩点后肯定是要重新构造一个有向无环图啦~ 那么怎么构图呢? 直接暴力枚举每条边如果不是在同一个强连通分量,那么很遗憾这条边不能重复走很多次,那么就两个强连通分量间连接一条

遍历

最后从S出发跑个dfs就好了。

Code

代码语言:javascript
复制
#include<bits/stdc++.h>
#define LD double
using namespace std;
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
struct edge{int u,v,w;}e[200010];
int n,m,fir[80010],nxt[200010],son[200010],w[200010],tot,sum[200010],val[200010],S,dis[80010],vis[80010],d[80010],t;
vector<int> G[80010],P[80010];
inline void add(int x,int y,int z){++tot;nxt[tot]=fir[x];fir[x]=tot;son[tot]=y;w[tot]=z;}
inline void dfs1(int x){
    vis[x]=1;
    for(auto i:G[x])//C++11简易写法
        if(!vis[i]) dfs1(i);
    d[++t]=x;
}
inline void dfs2(int x){
    vis[x]=t;
    for(auto i:P[x])
        if(!vis[i]) dfs2(i);
}
inline void kosaraju(){//缩点
    memset(vis,0,sizeof(vis));t=0;
    for(int i=1;i<=n;i++)
        if(!vis[i]) dfs1(i);
    memset(vis,0,sizeof(vis));t=0;
    for(int i=n;i>=1;i--)
        if(!vis[d[i]]) ++t,dfs2(d[i]);
}
inline void dfs(int x){
    if(dis[x]) return ;
    dis[x]=val[x];//别忘记加上点权
    int Max=0;
    for(int to,i=fir[x];i;i=nxt[i]){
        to=son[i];
        dfs(to);
        Max=max(Max,dis[to]+w[i]);
    }
    dis[x]+=Max;
}
int main(){
    n=read();m=read();
    for(int x,y,w,i=1;i<=m;i++){
        x=read();y=read();w=read();
        e[i]=(edge){x,y,w};
        G[x].push_back(y);
        P[y].push_back(x);//缩点需要建反边
        LD s;scanf("%lf",&s);
        while(w){//预处理出无限次走的边权
            sum[i]+=w;
            w=(LD)w*s;
        }
    }
    kosaraju();
    for(int i=1;i<=m;i++)
        if(vis[e[i].u]!=vis[e[i].v]) add(vis[e[i].u],vis[e[i].v],e[i].w);//不在同一个强连通分量,构图
        else val[vis[e[i].u]]+=sum[i];//在同一个强连通分量,累计点权
    S=read();
    dfs(vis[S]);//最后跑一次dfs求最大值
    printf("%d\n",dis[vis[S]]);
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-03-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Luogu P2656 采蘑菇 题解
    • Describe
      • Solution
        • 预处理
        • 找环&缩点
        • 构图
        • 遍历
      • Code
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档