题目:P2656 采蘑菇
Tarjan - 强连通分量
根据题意,我们把边分为两种,一种是强连通分量内部的,另一种是外面的
对于每个外面的边,只能走一次,因为不存在环
而对于每个内部的边,由于有环,可以走无数次,我们就把走无数次后得到的蘑菇数量算出来。因为回复指数小于等于 1 1 1,所以保证可以走有限次后可以把蘑菇全采完
最后,将每个强连通分量中最多能采到蘑菇数量,作为该点点权;最后跑一边点权 + 边权的最短路就行了
PS: 此题有重边,dijkstra会爆,要用SPFA
#include
#include
#include
#include
#include
#include
using namespace std;
const int Maxn=80020,inf=0x3f3f3f3f;
struct edge{
int v,len;
double p;
edge(int x,int y,double c)
{
v=x,len=y,p=c;
}
};
int dfn[Maxn],low[Maxn],f[Maxn];
int c[Maxn],dis[Maxn];
vector <edge> e[Maxn],g[Maxn];
bool vis[Maxn];
stack <int> s;
int n,m,ans,timecnt,st;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
void tarjan(int x)
{
dfn[x]=low[x]=++timecnt;
vis[x]=1,s.push(x);
for(int i=0;i<e[x].size();++i)
{
int y=e[x][i].v;
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(vis[y])low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x])
{
while(s.size())
{
int y=s.top();
s.pop();
vis[y]=0,f[y]=x;
if(x==y)break;
}
}
}
void work(int pos,int tot,double p)
{
while(tot>0)
{
c[pos]+=tot;
tot=int(tot*p);
}
}
void dij()
{
queue <int> q;
fill(dis+0,dis+2+n,-inf);
vis[st]=1,dis[st]=c[st];
q.push(st);
while(q.size())
{
int x=q.front();
q.pop();
vis[x]=0;
for(int i=0;i<g[x].size();++i)
{
int y=g[x][i].v;
if(dis[y]<dis[x]+g[x][i].len+c[y])
{
dis[y]=dis[x]+g[x][i].len+c[y];
if(!vis[y])vis[y]=1,q.push(y);
}
}
}
}
int main()
{
// freopen("in.txt","r",stdin);
n=read(),m=read();
for(int i=1;i<=m;++i)
{
int x=read(),y=read(),c=read();
double p;
scanf("%lf",&p);
e[x].push_back(edge(y,c,p));
}
for(int i=1;i<=n;++i)
if(!dfn[i])tarjan(i);
for(int i=1;i<=n;++i)
{
for(int j=0;j<e[i].size();++j)
{
int x=i,y=e[i][j].v;
if(f[x]==f[y])
{
work(f[x],e[i][j].len,e[i][j].p);
continue;
}
g[f[x]].push_back(edge(f[y],e[i][j].len,e[i][j].p));
}
}
st=read();
st=f[st];
dij();
for(int i=1;i<=n;++i)
ans=max(ans,dis[i]);
printf("%d\n",ans);
return 0;
}