POI2008MAF-Mafia

POI #基环树 #贪心 #Year2008

一定是若干棵基环内向树,以下仅考虑一棵的情况

最小值直接从下往上选,然后环上的为环的 \(siz\) 的一半

最大值为 \(n\) 减去叶子个数,再特判环上剩下一个的情况

// Author: xiaruize
const int N = 1e6 + 10;

int n;
int a[N];
int deg[N];
int cnt = 0, res = 0;
queue<int> q;
bool fl[N];
bool vis[N];

void solve()
{
	cin >> n;
	rep(i, 1, n)
	{
		cin >> a[i];
		deg[a[i]]++;
	}
	queue<int> q;
	rep(i, 1, n)
	{
		if (!deg[i])
		{
			q.push(i);
			cnt++;
			res++;
		}
	}
	while (!q.empty())
	{
		int x = q.front();
		q.pop();
		if (vis[a[x]])
			continue;
		vis[a[x]] = true;
		deg[a[a[x]]]--;
		fl[a[a[x]]] = true;
		if (!deg[a[a[x]]])
		{
			q.push(a[a[x]]);
			cnt++;
		}
	}
	rep(i, 1, n)
	{
		if (deg[i] && !vis[i])
		{
			int x = i;
			bool f = false;
			int len = 0;
			do
			{
				f |= fl[x];
				len++;
				vis[x] = true;
				x = a[x];
			} while (x != i);
			if (!f && len > 1)
				res++;
			cnt += len / 2;
		}
	}
	cout << n - cnt << ' ' << n - res << endl;
}

#ifndef ONLINE_JUDGE
bool end_of_memory_use;
#endif

signed main()
{
	// freopen(".in","r",stdin);
	// freopen(".out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int testcase = 1;
	// cin >> testcase;
	while (testcase--)
		solve();
#ifndef ONLINE_JUDGE
	cerr << "Memory use:" << (&end_of_memory_use - &start_of_memory_use) / 1024.0 / 1024.0 << "MiB" << endl;
	cerr << "Time use:" << (double)clock() / CLOCKS_PER_SEC * 1000.0 << "ms" << endl;
#endif
	return 0;
}
posted @ 2024-04-15 19:41  xiaruize  阅读(9)  评论(0)    收藏  举报