由题可以知道,每一条边都是有向边。一个检查站只能保护一个环上的路口,即检查站的个数也是环的个数。

题目的第一问求最小花费,也就是就每个环上建一个检查站的最小花费之和。因此用 tarjan 求出每一个强联通分量以及该分量内的最小花费,最后求一个和。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void tarjan (int u)//模板即可
{
low[u] = dfn[u] = ++times;
s.push (u);
for (int i = head[u];i;i = nxt[i])
{
int v = to[i];
if (!dfn[v])
{
tarjan (v);
low[u] = min (low[u],low[v]);
}
else if (!scc[v])
low[u] = min (low[u],dfn[v]);
}
if (low[u] == dfn[u])
{
++scc_cnt;
while (true)
{
int x = s.top ();
s.pop ();
scc[x] = scc_cnt;
mn[scc_cnt] = min (mn[scc_cnt],cost[x]);//求最小值
if (x == u) break;
}
}
}

最后相加时注意最小花费是不用取模的,所以答案为 $\sum^{n}_{i = 1} mn[i]$。

再来看第二问,求方案数。由于肯定要做到每一个强连通分量中选的检查站花费一定最优,所以只要求每一个分量中花费为 $mn[i]$ 的个数,然后根据乘法原理将它乘起来即可。注意这是需要取模的,答案为 $\prod^{n}_{i = 1} num[i]$。

1
2
3
4
for (int i = 1;i <= n;++i)
if (cost[i] == mn[scc[i]]) ++num[scc[i]];//计数
for (int i = 1;i <= scc_cnt;++i)
ans_cnt = (ll)ans_cnt * num[i] % MOD;//乘法原理