zoukankan      html  css  js  c++  java
  • 同桌与室友 计数类问题

    题意:

    题目描述:
    (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;
    }
    
  • 相关阅读:
    Android开发学习笔记-关于Android的消息推送以及前后台切换
    C#文件操作工具类
    c#取得应用程序根目录
    Visual Studio 2012中使用GitHub
    C#邮件发送
    jquery判断某个属性是否存在 hasAttr
    js 如何让两个等长的数组产生键值对关系
    js判断一个元素是否在数组中
    日期控件
    react 生命周期函数的一些心得体会
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/12218431.html
Copyright © 2011-2022 走看看