CF859E题解

题意简述

翻译很清楚了

题目解法

如果一个人想去的位置上原来坐着人,那么他要坐到这个位置上,就要把原来的人赶走。

原来的人被赶走了,就只能去想去的位置。如果那个位置上有人,又要把那个人赶走。

我们发现,如果将每个人原来的位置连一条单向边到想要的位置上,那么这种关系似乎是一棵树。每一个人能换,当且仅当他的父亲能换

这种关系会在什么时候结束呢?第一种情况,到了一个没人坐的节点。这种情况当然皆大欢喜,树上任何一个人,只要他想换,都能换成功(但最多只能成功一个人,因为另一个人如果也想换,就必定会有一个位置上挤着两个想要换的人)。

第二种情况,到了树上一个节点。那么这时就不是树了,这是一棵基环树。对于一棵基环树,我们只有全换和全不换两种情况。同时,树上不属于环的节点肯定都不能换。因为如果换了,那么环上节点必须要换,因为根节点换了。此时必定不会合法。

如果是自环呢?那么这个节点相当于不能换。不能换,也就不能被赶走。这棵树只有一种情况,就是都不换。

这个题就变成了简单的乘法原理。

注意,位置是人的两倍,输入的第一个数是人数

我们可以用并查集查找根节点。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e9 + 7;
const ll maxn = 2e5 + 5;
ll n;
ll fa[maxn], si[maxn], a[maxn];
bool ton[maxn];
ll find(ll u){ return (fa[u] == u) ? fa[u] : fa[u] = find(fa[u]); }
signed main(){
	freopen("text.in", "r", stdin);
	scanf("%lld", &n);
	for(ll i = 1;i <= 2 * n;i ++) fa[i] = i, si[i] = 1;
	ll u, v;
	for(ll i = 1;i <= n;i ++)
	{
		scanf("%lld%lld", &u, &v);
		a[u] = v;	
		if(u == v) continue;
		ll U = find(u), V = find(v);
		// cout << u << ' ' << v << ' ' << U << ' ' << V << endl;
		if(U == V)
		{
			si[V] = -1;
		}
		else
		{
			fa[U] = V;
			if(si[V] != -1) si[V] += si[U];//每一个人开始的位置是不同的,所以只有根节点可能形成环
		}
	}
	ll ans = 1;
	for(ll i = 1;i <= 2 * n;i ++)
	{
		ll zu = find(i);
		if(ton[zu] == 1 || (a[zu] == zu) || si[zu] == -2) continue;
		ton[zu] = 1;
		if(si[zu] == -1) si[zu] = 2;
		ans *= si[zu];
		ans %= mod;
	}
	printf("%lld\n", ans);
	return 0;
}
posted @ 2022-10-24 16:49  _maze  阅读(27)  评论(0)    收藏  举报