遍历(强连通)
第3题 遍历 查看测评数据信息
给定n个点,m条边的有向图,每个节点有一个权重v(i),你的任务是把n个点遍历一遍,点可以重复经过。允许的操作如下:每次你可以选择一个开始节点i,如果可以从i出发,最后可以回到i节点,那么你的花费为v(i)。
问:最少需要多少费用可以把n个节点遍历一遍,并且当费用最少时的方案数是多少?存在一个开始节点不同被视为两种不同的方案,由于方案数可能过大,所以请输出方案数对 10^9+7 取模的结果。
(注:这里每次可以从任何一个开始节点开始走回路。就是说一个回路走完了,下一个开始位置可以任选。)
对于 100% 的数据,1<= n <= 10^5,1<= m <= 3*10^5,0<= w(i) <= 10^9。
输入格式
第一行一个正整数 n。
第二行 n 个正整数,表示每个点的点权 w(i)。
第三行一个正整数 m。
接下来 m 行,每行两个正整数 x,y,表示一条 x到y有一条有向边。
输出格式
输出一行两个整数,分别表示最小花费,和花费最小时的方案数。
输入/输出例子1
输入:
3
1 2 3
3
1 2
2 3
3 2
输出:
3 1
样例解释
无
比较简单,记录缩点后胖点内最小值即可,以及有几个最小值
#include <bits/stdc++.h> using namespace std; const int N=1e5+5, Mod=1e9+7; int n, m, w[N], u1, v1; int dfn[N], low[N], idx=0, cnt=0, id[N], Min[N]; long long Aans=0, Bans=0; stack<int> st; vector<int> a[N], b[N]; void dfs(int u) { dfn[u]=low[u]=++idx; st.push(u); for (int i=0; i<a[u].size(); i++) { int v=a[u][i]; if (!dfn[v]) { dfs(v); low[u]=min(low[u], low[v]); } else if (!id[v]) low[u]=min(low[u], dfn[v]); } if (dfn[u]==low[u]) { cnt++; while (st.top()!=u) { id[st.top()]=cnt; b[cnt].push_back(w[st.top()]); Min[cnt]=min(Min[cnt], w[st.top()]); st.pop(); } id[st.top()]=cnt; b[cnt].push_back(w[st.top()]); Min[cnt]=min(Min[cnt], w[st.top()]); st.pop(); } } int main() { for (int i=0; i<N-5; i++) Min[i]=1e9+5; scanf("%d", &n); for (int i=1; i<=n; i++) scanf("%d", &w[i]); scanf("%d", &m); for (int i=1; i<=m; i++) { scanf("%d%d", &u1, &v1); a[u1].push_back(v1); } for (int i=1; i<=n; i++) if (!dfn[i]) dfs(i); /* for (int i=1; i<=cnt; i++) { printf("%d: {", i); for (int j=0; j<b[i].size(); j++) printf("%d ", b[i][j]); printf("}\n"); }*/ Bans=1; for (int i=1; i<=cnt; i++) { int s=0; for (int j=0; j<b[i].size(); j++) if (b[i][j]==Min[i]) s++; Bans=(Bans*s)%Mod; Aans+=Min[i]; } printf("%lld %lld", Aans, Bans); return 0; } /* 1: {3 2 } 2: {1 } 3 1 2 3 3 1 2 2 3 3 2 */