洛咕

题意:有n座电影院,n对情侣分别在每座电影院里,然后电影院里都有汽油,但是要使用它需要一定的费用.m条单向通道连接相邻的两对情侣所在电影院.然后HXY有个绝技,如果她能从一个点开始烧,最后回到这个点,那么烧这条回路上的情侣的费用只需要该点的汽油费即可.并且每对情侣只需烧一遍,电影院可以重复去.然后她想花尽可能少的费用烧掉所有的情侣.问最少需要多少费用,并且当费用最少时的方案数是多少?由于方案数可能过大,所以请输出方案数对\(1e9+7\)取模的结果.

(注:这里HXY每次可以从任何一个点开始走回路。就是说一个回路走完了,下一个开始位置可以任选.所以说不存在烧不了所有情侣的情况,即使图不连通,HXY自行选择顶点进行烧情侣行动。且走过的道路可以重复走.)

分析:根据题意,一个强连通分量里面的点可以同时烧掉,费用为里面所有点的最小值.所以我们跑个有向图\(Tarjan\)缩点,同时记录每个强连通分量里面的费用最小值以及有几个费用最小值的点(便于输出方案,方案就是每个强连通分量 费用最小值的个数的乘积).

然后直接枚举每个强连通分量,分别计算答案即可.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int mod=1e9+7;
const int N=100005;
const int M=300005;
ll ans1,ans2=1;
int n,m,w[N],a[M],b[M];
int tim,top,num,dfn[N],low[N],st[N],color[N],val[N][2];
//val[i][0]表示第i个强连通分量的费用最小值
//val[i][1]表示第i个强连通分量的费用最小值的个数
int tot,head[N],nxt[M],to[M];
inline void add(int a,int b){
    nxt[++tot]=head[a];head[a]=tot;to[tot]=b;
}
inline void tarjan(int u){
    dfn[u]=low[u]=++tim;st[++top]=u;
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!color[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u]){
        color[u]=++num;
        val[num][0]=w[u];val[num][1]=1;
        while(st[top]!=u){
            color[st[top]]=num;
            if(w[st[top]]<val[num][0]){
                val[num][0]=w[st[top]];
                val[num][1]=1;
            }
            else if(w[st[top]]==val[num][0])++val[num][1];
            --top;
        }
        --top;
    }
}
int main(){
    n=read();for(int i=1;i<=n;++i)w[i]=read();
    m=read();
    for(int i=1;i<=m;++i){
        a[i]=read();b[i]=read();
        add(a[i],b[i]);
    }
    for(int i=1;i<=n;++i)if(!dfn[i])tarjan(i);
    for(int i=1;i<=num;++i){
        ans1+=val[i][0];//累加每个强连通分量的费用最小值
        ans2=(ans2*val[i][1])%mod;//乘法原理计算方案
    }
    printf("%lld %lld\n",ans1,ans2);
    return 0;
}
01-14 11:58