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;
}

浙公网安备 33010602011771号