Codeforces 859E Desk Disorder:并查集【两个属性二选一】

题目链接:http://codeforces.com/problemset/problem/859/E

题意:

  有n个人,2n个座位。

  给出这n个人初始的座位,和他们想坐的座位。

  每个人要么坐在原来的位置不动,要么坐到想坐的座位上,但是不能有两个人坐在同一个座位上。

  问你合法的安排座位的方案数。

 

题解:

  将2n个座位抽象成2n个点。

  对于每个人,从他的初始位置向想坐的位置连一条边。

  总答案即为所有连通块答案的乘积。

  由于每一个点最多向外连一条边,所以对于每一个连通块只有三种情况:

    (1)是一棵树,根节点不自环,且所有边的方向都是由儿子指向父亲。

    (2)是一棵树,根节点自环,且所有边的方向都是由儿子指向父亲。

    (3)有且只有一个环。

  对于这三种情况,可以发现:

    (1)方案数 = 连通块大小siz[fa]

    (2)方案数 = 1

    (3)方案数 = 2

  并查集维护一下,最后统计答案即可。

 

AC Code:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <algorithm>
 5 #define MAX_N 200005
 6 #define MOD 1000000007
 7 
 8 using namespace std;
 9 
10 int n;
11 int par[MAX_N];
12 int siz[MAX_N];
13 bool tag[MAX_N];
14 bool loop[MAX_N];
15 long long ans=1;
16 
17 void init_union_find()
18 {
19     for(int i=1;i<=(n<<1);i++)
20     {
21         par[i]=i;
22         siz[i]=1;
23         tag[i]=false;
24         loop[i]=false;
25     }
26 }
27 
28 int find(int x)
29 {
30     return par[x]==x ? x : par[x]=find(par[x]);
31 }
32 
33 void unite(int x,int y)
34 {
35     int px=find(x);
36     int py=find(y);
37     if(px==py)
38     {
39         tag[px]=true;
40         return;
41     }
42     siz[py]+=siz[px];
43     tag[py]|=tag[px];
44     par[px]=py;
45 }
46 
47 void read()
48 {
49     cin>>n;
50     init_union_find();
51     int x,y;
52     for(int i=1;i<=n;i++)
53     {
54         cin>>x>>y;
55         unite(x,y);
56         if(x==y) loop[x]=true;
57     }
58 }
59 
60 void work()
61 {
62     for(int i=1;i<=(n<<1);i++)
63     {
64         if(find(i)==i)
65         {
66             if(loop[i]) continue;
67             if(tag[i]) ans=(ans<<1ll)%MOD;
68             else ans=ans*siz[i]%MOD;
69         }
70     }
71     cout<<ans<<endl;
72 }
73 
74 int main()
75 {
76     read();
77     work();
78 }

 

转载于:https://www.cnblogs.com/Leohh/p/8473751.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值