洛谷P2194 HXY烧情侣

题目描述

众所周知,HXY已经加入了FFF团。现在她要开始喜(sang)闻(xin)乐(bing)见(kuang)地烧情侣了。这里有n座电影院,n对情侣分别在每座电影院里,然后电影院里都有汽油,但是要使用它需要一定的费用。m条单向通道连接相邻的两对情侣所在电影院。然后HXY有个绝技,如果她能从一个点开始烧,最后回到这个点,那么烧这条回路上的情侣的费用只需要该点的汽油费即可。并且每对情侣只需烧一遍,电影院可以重复去。然后她想花尽可能少的费用烧掉所有的情侣。问最少需要多少费用,并且当费用最少时的方案数是多少?由于方案数可能过大,所以请输出方案数对1e9+7取模的结果。
(注:这里HXY每次可以从任何一个点开始走回路。就是说一个回路走完了,下一个开始位置可以任选。所以说不存在烧不了所有情侣的情况,即使图不连通,HXY自行选择顶点进行烧情侣行动。且走过的道路可以重复走。)

输入输出格式

输入格式:

第一行,一个整数n。
第二行,n个整数,表示n个情侣所在点的汽油费。
第三行,一个整数m。
接下来m行,每行两个整数xi,yi,表示从点xi可以走到yi。

输出格式:

一行,两个整数,第一个数是最少费用,第二个数是最少费用时的方案数对1e9+7取模

输入输出样例

输入样例1

3
1 2 3
3
1 2
2 3
3 2

输入样例2

3
10 20 10
4
1 2
1 3
3 1
2 1

输出样例1

3 1

输出样例2

10 2

说明

数据范围:
对于30%的数据,1<=n,m<=20;
对于10%的数据,保证不存在回路。
对于100%的数据,1<=n<=100000,1<=m<=300000。所有输入数据保证不超过10^9。
tarjan 求 scc 的裸题,每个 scc 里取个最小值, Σscc_min[ i ] 为第一问答案,记录下每个 scc 里最小的点的个数,乘起来是第二问答案。
代码如下

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define ll long long
using namespace std;
const int sz = 800010;
const int mod = 1e9+7;
ll head[sz],nxt[sz],dist[sz],l[sz];
ll low[sz],dfn[sz],scc_num,dfs_clock;
ll scc_min[sz];
struct gtnd
{
    ll p,num;
    bool operator <(const gtnd &a)const
    {
        return num < a.num;
    }
}scc[sz];
int len;
int tot = 1;
ll ans_min;
ll ans_time = 1;
stack < int > s;
int n,m;
void build(int f,int t)
{
    l[tot] = t;
    nxt[tot] = head[f];
    head[f] = tot ++;
}
ll read()
{
    ll x = 0 , f = 1;
    char in = getchar();
    while(in < '0' || in > '9')
    {
        if(in == '-')
            f = -1;
        in = getchar();
    }
    while('0' <= in && in <= '9')
    {
        x = x * 10 + in - '0';
        in = getchar();
    }
    return x * f;
}
void start_work()
{
    n = read();
    for(int i = 1 ; i <= n ; i ++)
        dist[i] = read();
    m = read();
    for(int i = 1 ; i <= m ; i ++)
    {
        int xi = read() , yi = read();
        build(xi,yi);
    }
}
int tarjan(int u)
{
    dfn[u] = low[u] = dfs_clock ++;
    s.push(u);
    for(int i = head[u] ; i ; i = nxt[i])
    {
        int v = l[i];
        if(!dfn[v])
        {
            low[v] = tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(!scc[v].num)
            low[u] = min(low[u],dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        scc_num ++;
        while(12 < 450)
        {
            if(s.empty())
                break;
            int v = s.top();
            s.pop();
            scc[v].num = scc_num;
            scc[v].p = v;
            if(u == v)
                break;
        }
    }
    return low[u];
}
void make_tarjan()
{
    for(int i = 1 ; i <= n ; i ++)
        if(!dfn[i])
            tarjan(i);
    sort(scc+1,scc+n+1);
}
void work()
{
    for(int i = 1 ; i <= n ; i ++)
    {
        int j;
        for(j = i + 1 ; j <= n + 1 ; j ++)
            if(scc[i].num != scc[j].num)
                break;
        j --;
        int minn = 2147483641 , minn_time = 0;
        for(int k = i ; k <= j ; k ++)
            if(minn > dist[scc[k].p])
                minn = dist[scc[k].p] , minn_time = 1;
            else if(minn == dist[scc[k].p])
                minn_time ++;
        scc_min[++len] = minn_time;
        ans_min += minn;
        i = j;
    }
    for(int i = 1; i <= len ; i ++)
        ans_time *= scc_min[i] % mod , ans_time %= mod;
    printf("%lld %lld\n",ans_min,ans_time);
}
int main()
{
    start_work();
    make_tarjan();
    work();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章