歌名 - 歌手
0:00

    【NOIP2013模拟联考6】选课

    题目

    你真的认为选课是那么容易的事吗?HYSBZ的ZY同志告诉你,原来选课也会让人产生一种想要回到火星的感觉。假设你的一周有n天,那么ZY编写的选课系统就会给你n堂课。但是该系统不允许在星期i和星期i+1的时候选第i堂课,也不允许你在星期n和星期一的时候选第n堂课。然后连你自己也搞不清哪种选课方案合法,哪种选课不合法了。你只想知道,你到底有多少种合法的选课方案。

    分析

    声明一下,参考了题解。
    我们定义\(W_k\)表示至少有k节课选错的方案数,
    求出这个,用容斥原理就很容易求出正确选课方案数量。
    那怎么求\(W_k\)呢?
    现在总共 n 堂课分别记为 1,2,...n,它们可放的天数可以表示为(1,2)(2,3)(3,4)...(n,1)现在我们把括号去掉即得到一个数列 1,2,2,3,3,4,....n,1,现在我们从里面取出 K 个数,分别表示 k 堂课所在的天数,现在只要求出满足这个条件的取法数就可以了。
    但是,我们不能再同一天选两节课,那么第\(i*2-1和第i*2\)个数是不可以同时取的,
    接着,我们不能重复取一节课,那么第\(i*2和第i*2+1\)个数是不可以同时取的(当然,最后一个“1”和最前面的“1”也是不可以同时取的)。
    也就是说,对于一个环,求从其中选取k个不相邻顶点得方案数:\(C_{2n-k-1}^{k-1}*\dfrac{2n}{k}\)
    证明:对于任意一个顶点A,先取A,然后再从不和A相邻的n-3个其他顶点中取k-1个不相邻顶点,显然可得到符合定理要求的组合,这个组合的个数为C((n-3)-(k-1)+1,k-1)=C(n-k-1,k-1)。一共有n个顶点,而且在每个组合中有k个元素,即可完成证明。
    然后其余的随便选,
    所以\(W_{k}=C_{2n-k-1}^{k-1}*\dfrac{2n}{k}*(n-k)!\)
    THEN?容斥。
    据说有种更强大的方法:这道提示有递推式滴!!!
    这里写图片描述
    但方法实在是太神奇了,根本就搞不懂。+_+

    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    const long long maxlongint=2147483647;
    const long long mo=1000000007;
    using namespace std;
    long long jc[210000],ans,n,ny[210000],nn[210000];
    long long mi(long long x,long long y)
    {
    	long long sum=1;
    	while(y)
    	{
    		if(y&1) sum=sum*x%mo;
    		x=x*x%mo;
    		y/=2;
    	}
    	return sum;
    }
    long long C(long long m,long long n)
    {
    	return jc[m]*nn[m-n]%mo*nn[n]%mo;
    }
    long long W(long long k)
    {
    	return n*2*ny[n*2-k]%mo*C(n*2-k,k)%mo*jc[n-k]%mo;
    }
    int main()
    {
    	for(long long i=0;i<=200005;i++)
    		ny[i]=mi(i,mo-2);
    	jc[0]=1;
    	nn[0]=1;
    	for(long long i=1;i<=200005;i++)
    	{
    		jc[i]=jc[i-1]*i%mo;
    		nn[i]=mi(jc[i],mo-2);
    	}
    	while(scanf("%lld",&n)!=EOF)
    	{
    		if(n==1)
    		{
    			printf("0\n");
    			continue;
    		}
    		ans=jc[n];
    		for(long long i=1;i<=n;i++)
    		{
    			if(i%2)
    				ans=(ans-W(i)+mo)%mo;
    					else 
    						ans=(ans+W(i))%mo;
    		}
    		printf("%lld\n",ans);
    	}
    }
    
    
    
    posted @ 2018-05-12 19:54  无尽的蓝黄  阅读(215)  评论(0编辑  收藏  举报