1027D. Mouse Hunt(1700)

分析:每个点有且仅有一条出边,也就是基环内向树。
所有的老鼠最终都会跑到环里,我们只要把捕鼠夹放在这个环里的最小花费的地方,就可以捕捉所有老鼠。

我们使用有向图的强连通分量,出度为0的强连通分量就是基环。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;
const int N = 200005;
const int M = 200005;
int dfn[N], low[N], timestamp;
int stk[N], in_stk[N], top;
int scc_cnt;
int id[N];
int h[N], e[M], ne[M], idx;
int c[N];
vector<int> scc[N];
vector<int> v[N];
int d[N];
void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void tarjan(int u)
{
	dfn[u] = low[u] = ++timestamp;
	stk[++top] = u, in_stk[u] = true;
	for (int i = h[u]; i != -1; i = ne[i])
	{
		int j = e[i];
		if (!dfn[j])
		{
			tarjan(j);
			low[u] = min(low[u], low[j]);
		}
		else if (in_stk[j])
			low[u] = min(low[u], dfn[j]);
	}

	if (dfn[u] == low[u])
	{
		int y;
		++scc_cnt;
		do {
			y = stk[top--];
			in_stk[y] = false;
			id[y] = scc_cnt;
			scc[scc_cnt].push_back(y);
		} while (y != u);
	}
}


int main()
{
	int n;
	scanf("%d", &n);

	for (int i = 1; i <= n; ++i) scanf("%d", &c[i]);

	memset(h, -1, sizeof h);

	int a;
	for (int i = 1; i <= n; ++i)
	{
		scanf("%d", &a);
		add(i, a);
	}

	for (int i = 1; i <= n; ++i)
		if (!dfn[i])
			tarjan(i);

	for (int i = 1; i <= n; ++i)
	{
		v[id[i]].push_back(c[i]);
		for (int j = h[i]; j != -1; j = ne[j])
		{
			int k = e[j];
			if (id[i] != id[k]) ++d[id[i]];
		}
	}

	long long res = 0;
	for (int i = 1; i <= scc_cnt; ++i)
	{
		if (d[i] == 0)
		{
			sort(v[i].begin(), v[i].end());
			res += v[i][0];
		}
	}

	printf("%lld\n", res);

	return 0;
}