Codeforces Round #788 (Div. 2) Where is the Pizza?(并查集+环)

https://codeforces.com/contest/1670/problem/C

这位佬写的挺不错的,强推
https://blog.csdn.net/qq_42883649/article/details/124644866?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166011975416782184630508%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=166011975416782184630508&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-2-124644866-null-null.142v40pc_rank_34,185v2control&utm_term=C.%20Where%20is%20the%20Pizza&spm=1018.2226.3001.4187

给定长度为n的两种排列a和b。排列是由从1到n的n个不同整数以任意顺序组成的数组。

开始摆弄这两种排列。第一个排列的一些元素和第二个排列的一些元素混在了一起,令他惊讶的是,那些元素也形成了一个大小为n的排列。

对于每个i (1≤i≤n),他要么使ci=ai,要么使ci=bi。
数组c是一个排列。
你知道排列a,b,和c中某些位置的值。请计算与所描述的过程和给定值一致的不同排列c的数量。既然答案可以很大,那就模10^9+7打印出来。

保证至少存在一个满足所有要求的置换c。
input
9
7
1 2 3 4 5 6 7
2 3 1 7 6 5 4
2 0 1 0 0 0 0
1
1
1
0
6
1 5 2 4 6 3
6 5 3 1 4 2
6 0 0 0 0 0
8
1 6 4 7 2 3 8 5
3 2 8 1 4 5 6 7
1 0 0 7 0 3 0 5
10
1 8 6 2 4 7 9 3 10 5
1 9 2 3 4 10 8 6 7 5
1 9 2 3 4 10 8 6 7 5
7
1 2 3 4 5 6 7
2 3 1 7 6 5 4
0 0 0 0 0 0 0
5
1 2 3 4 5
1 2 3 4 5
0 0 0 0 0
5
1 2 3 4 5
1 2 3 5 4
0 0 0 0 0
3
1 2 3
3 1 2
0 0 0
output
4
1
2
2
1
8
1
2
2
#include<bits/stdc++.h>
using namespace std;
const int N=200200,M=2002;
const int mod=1e9+7;
int a[N],b[N],c[N];
int father[N],siz[N];//siz用来记录每个并查集的大小(如果为2则贡献为0)
bool is_valid[N];
int find(int x)
{
    if(father[x]!=x) father[x]=find(father[x]);
    return father[x];
}
int main()
{
    cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);
    int T=1;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            father[i]=i;
            is_valid[i]=true;
            siz[i]=0;
        }
        for(int i=1;i<=n;i++)
            cin>>a[i];
        for(int i=1;i<=n;i++)
            cin>>b[i];
        for(int i=1;i<=n;i++)
            cin>>c[i];
        for(int i=1;i<=n;i++)
        {
            //建环,合并a和b所在的两个集合
            father[find(a[i])]=find(b[i]);
        }
        for(int i=1;i<=n;i++)
        {
            //如果这个数字已经固定住的话,就让它不能再运算了,因为一个环只有两种遍历方式,但是固定住了哪怕只有一个,那也就是只能是同一种填写方式
            if(c[i]!=0) is_valid[find(a[i])]=false;

            //找到每个数字的祖宗节点上数字增加2。用于判断是否自环
            siz[find(a[i])]+=2; //???
        }
        int res=1;
        for(int i=1;i<=n;i++)
        {
            int m=find(a[i]);//找到它祖宗
            if(is_valid[m]==true)//发现没有上锁,可以凑出答案
            {
                //自环只有一种选择,对答案没有贡献
                if(siz[m]>2) res=(res*2)%mod;
            }
            is_valid[m]=false;//这个环找完了,换下一个
        }
        cout<<res<<endl;
    }
    return 0;
}
posted @ 2022-08-10 17:01  Vijurria  阅读(25)  评论(0编辑  收藏  举报