1. 程式人生 > >洛谷P2194 HXY燒情侶

洛谷P2194 HXY燒情侶

題目描述

眾所周知,\(HXY\)已經加入了\(FFF\)團。現在她要開始喜\((sang)\)\((xin)\)\((bing)\)\((kuang)\)地燒情侶了。這裡有\(n\)座電影院,\(n\)對情侶分別在每座電影院裡,然後電影院裡都有汽油,但是要使用它需要一定的費用。\(m\)條單向通道連線相鄰的兩對情侶所在電影院。然後\(HXY\)有個絕技,如果她能從一個點開始燒,最後回到這個點,那麼燒這條迴路上的情侶的費用只需要該點的汽油費即可。並且每對情侶只需燒一遍,電影院可以重複去。然後她想花盡可能少的費用燒掉所有的情侶。問最少需要多少費用,並且當費用最少時的方案數是多少?由於方案數可能過大,所以請輸出方案數對\(1e9+7\)

取模的結果。

(注:這裡\(HXY\)每次可以從任何一個點開始走迴路。就是說一個迴路走完了,下一個開始位置可以任選。所以說不存在燒不了所有情侶的情況,即使圖不連通,\(HXY\)自行選擇頂點進行燒情侶行動。且走過的道路可以重複走。)

輸入輸出格式

輸入格式:

第一行,一個整數\(n\)

第二行,\(n\)個整數,表示\(n\)個情侶所在點的汽油費。

第三行,一個整數\(m\)

接下來\(m\)行,每行兩個整數\(x_i,y_i\),表示從點xi可以走到\(y_i\)

輸出格式:

一行,兩個整數,第一個數是最少費用,第二個數是最少費用時的方案數對\(1e9+7\)取模

輸入輸出樣例

輸入樣例#1:

3
1 2 3
3
1 2
2 3
3 2

輸出樣例#1:

3 1

輸入樣例#2:

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

輸出樣例#2:

10 2

說明

資料範圍:

對於\(30\%\)的資料,\(1<=n,m<=20\)

對於\(10\%\)的資料,保證不存在迴路。

對於\(100\%\)的資料,\(1<=n<=100000,1<=m<=300000\)。所有輸入資料保證不超過\(10^9\)

思路:考慮tarjan縮點,把一個強連通分量縮成一個點的同時更新這個強連通分量裡汽油費的最小值,最後每個強連通分量的最小值的和就是第一個子問題的答案,然後看看每個聯通塊中有多少個權值是它那個更新出來的最小值,根據乘法原理,把每個強連通分量得到結果乘起來就是第二個子問題的答案。

程式碼:

#include<cstdio>
#include<algorithm>
#include<cctype>
#include<stack>
#define maxn 100007
#define ll long long
using namespace std;
const ll mod=1e9+7;
int n,m,head[maxn],w[maxn],dfn[maxn],block[maxn],low[maxn],bel[maxn],cnt,js,num,ans;
bool vis[maxn];
ll zrj=1,size[maxn];
struct node{
  int v,nxt;
}e[300007];
inline void ct(int u, int v) {
  e[++num].v=v;
  e[num].nxt=head[u];
  head[u]=num;
}
stack<int>q;
void tarjan(int u) {
  dfn[u]=low[u]=++cnt;
  q.push(u),vis[u]=1;
  for(int i=head[u];i;i=e[i].nxt) {
    int v=e[i].v;
    if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
    else if(vis[v]) low[u]=min(low[u],dfn[v]);
  }
  if(dfn[u]==low[u]) {
    int x=-1;js++;
    block[js]=w[u];
    while(x!=u) {
      x=q.top(),q.pop();
      block[js]=min(block[js],w[x]);
      bel[x]=js,vis[x]=0;
    }
  }
}
int main() {
  scanf("%d",&n);
  for(int i=1;i<=n;++i) scanf("%d",&w[i]);
  scanf("%d",&m);
  for(int i=1,u,v;i<=m;++i) {
    scanf("%d%d",&u,&v);
    ct(u,v);
  }
  for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
  for(int i=1;i<=js;++i) ans+=block[i];
  printf("%d ",ans);
  for(int i=1;i<=n;++i) if(w[i]==block[bel[i]]) size[bel[i]]++;
  for(int i=1;i<=js;++i) {
    zrj*=size[i];
    zrj%=mod;
  }
  printf("%d\n",zrj);
  return 0;
}