思路: 根据tarjan算法求最强连通分支,然后找出每个联通分支中的最小值,并统计其数量,乘法定理解决方法数。
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> #include <vector> #include <stack> using namespace std; #define MEM(a,b) memset(a, b, sizeof(a)) #define _FOR_I_(_offx, _offy, _offz) for(i=_offx; i<=_offy; i+=_offz) #define INT64 __int64 const int N = 100100; const int E = 300300; const int INF = 0x7fffffff; const int MODNUM = 1000000007; struct Node{ int m, num; }; int n, val[N]; vector<int> v[N]; int scc_cnt, dfs_clock; int DFN[N], cmp[N], low[N]; stack<int> s; Node res[N]; void tarjan(int u){ DFN[u] = low[u] = ++dfs_clock; s.push(u); for(int i = 0; i < v[u].size(); ++ i){ if(!DFN[ v[u][i] ]) { tarjan(v[u][i]); low[u] = min(low[u], low[ v[u][i] ]); } else if(!cmp[ v[u][i] ]){ low[u] = min(low[u], DFN[ v[u][i] ]); } } if(low[u] == DFN[u]){ ++ scc_cnt; for(;;){ int x = s.top(); s.pop(); cmp[x] = scc_cnt; if(x == u) break; } } } void scc(){ dfs_clock = scc_cnt = 0; MEM(DFN, 0); MEM(cmp, 0); int i; _FOR_I_(1, n, 1) { if(!DFN[i]) tarjan(i); } } int main() { scanf("%d", &n); for(int i = 1; i <= n; i ++) scanf("%d", &val[i]); int m; scanf("%d", &m); while(m --) { int f, t; scanf("%d%d", &f, &t); v[f].push_back(t); } // tarjan method to find strongly connected components scc(); int i; _FOR_I_(1, scc_cnt, 1) res[i].m = INF; // find minimum money for each strongly connected components _FOR_I_(1, n, 1){ if(res[ cmp[i] ].m == val[i]) ++res[ cmp[i] ].num; if(res[ cmp[i] ].m > val[i]){ res[ cmp[i] ].m = val[i]; res[ cmp[i] ].num = 1; } } // count the value INT64 sum_val = 0, sum = 1; _FOR_I_(1, scc_cnt, 1){ sum_val += res[i].m; sum = sum * res[i].num % MODNUM; } printf("%I64d %I64d\n", sum_val, sum); return 0; }