• 同桌与室友 计数类问题


    题意:

    题目描述:
    (X)班有 N 个人,从(1)(N)编号。他们中有一些人住双人宿舍,一些人住单间,也
    就是说一些人有唯一的一个室友,有些人则没有。同时有些人会和他的同桌共用一张双
    人桌,另一些人则单独坐。
    你需要求出有多少个排列(P),满足原本的人(i)换到(P_i)的宿舍以及桌子上后,原本
    的室友以及同桌关系依旧不变,答案对(10^9 + 7)取模。
    输入格式
    第一行三个整数(N,M1,M2),表示人数,双人宿舍数量,双人桌数量。
    接下来(M1)行,每行两个整数(x,y),表示(x)(y)同住一间双人宿舍。
    接下来(M2)行,每行两个整数(x,y),表示(x)(y)同用一张双人桌。
    输出格式
    输出一行一个整数,表示满足条件的排列数量。
    样例 1 输入
    7 2 2
    1 2
    3 4
    1 4
    5 6
    样例 1 输出
    4
    样例 2 输入
    5 2 1
    1 2
    3 4
    1 4
    2
    样例 2 输出
    2
    数据范围与约定
    对于(5)%的数据,(1 ≤ N ≤ 1);
    对于 (20)% 的数据,(1 ≤ N ≤ 10);
    对于额外(15)% 的数据,一个人要么住单人间,要么就用单人桌;
    对于额外 (15)% 的数据, 保证 (N) 是偶数,(M1 = M2 =N/2)
    对于额外 (20)% 的数据, 保证数据完全随机生成;
    对于(100)% 的数据,保证 (1 ≤ N ≤ 2 × 10^5,M1, M2 ≤N/2)

    一句话版:一张图有黑白两种边,求使得图同构的置换数

    思路:

    将双人宿舍关系看做是黑边,双人桌关系看做是白边,那么就是一个图同构的计数
    考试时一看是图同构,打了个暴力就跑
    其实这道题的图有特殊之处
    每个点度数最多为2(白边黑边各一条)
    因此每一个连通块要么是一个简单环,要么是一条链
    分类计数即可。
    具体来说,
    如果有 (x) 个大小为 (i) 的环,那么这一部分答案为 (x! × i^x)
    如果有 (x) 个长度相同,两段边同为黑或同为白的链,那么这一部分答案为 (x! × 2^x)
    如果有 (x) 个长度相同,两段边不同色的链,那么这一部分答案为 (x!)

    注意事项:

    做一道题最好能把题意极为简洁地概括出来,方便分析
    牢牢抓住一道题的特殊点

    code:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+5;
    const int p=1e9+7;
    int n,m1,m2,rg,cnt,cl,ans=1;
    int to[N][2],r[N],l[N][2];
    bool vis[N];
    inline int read()
    {
    	int s=0,w=1; char ch=getchar();
    	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
    	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
    	return s*w;
    }
    void dfs(int u,int tp)
    {
    	vis[u]=true;++cnt;
    	if(tp==-1)
    	{
    		if(to[u][0]&&!vis[to[u][0]]) dfs(to[u][0],0);
    		if(to[u][1]&&!vis[to[u][1]]) dfs(to[u][1],1);
    		return;
    	}
    	if(!to[u][tp^1]){rg=1;cl=tp;return;}
    	if(vis[to[u][tp^1]]) return;
    	dfs(to[u][tp^1],tp^1);
    }
    int main()
    {
    	n=read(),m1=read(),m2=read();
    	for(int i=1;i<=m1;++i)
    	{
    		int x=read(),y=read();
    		to[x][0]=y,to[y][0]=x;
    	}
    	for(int i=1;i<=m2;++i)
    	{
    		int x=read(),y=read();
    		to[x][1]=y,to[y][1]=x;
    	}
    	for(int i=1;i<=n;++i)
    	{
    		if(vis[i]) continue;
    		rg=0;cnt=0;dfs(i,-1);
    		if(!rg)
    		{
    			ans=1ll*ans*cnt%p;
    			++r[cnt];
    			ans=1ll*ans*r[cnt]%p;
    		}
    		else
    		{
    			if(cnt&1)
    			{
    				++l[cnt][0];
    				ans=1ll*ans*l[cnt][0]%p;
    			}
    			else
    			{
    				ans=ans*2ll%p;
    				++l[cnt][cl];
    				ans=1ll*ans*l[cnt][cl]%p;
    			}
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    查找(线性索引)
    查找(顺序表&有序表)
    数据结构图之六(关键路径)
    数据结构图之五(拓扑排序)
    数据结构图之四(最短路径--弗洛伊德算法)
    数据结构图之三(最短路径--迪杰斯特拉算法)
    数据结构图之二(最小生成树--克鲁斯卡尔算法)
    数据结构图之二(最小生成树--普里姆算法)
    数据结构图之一(基本概念,存储结构,两种遍历)
    数据结构--堆
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/12218431.html
Copyright © 2020-2023  润新知