遍历(强连通)
第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
*/

浙公网安备 33010602011771号