Description
你真的认为选课是那么容易的事吗?HYSBZ的ZY同志告诉你,原来选课也会让人产生一种想要回到火星的感觉。假设你的一周有n天,那么ZY编写的选课系统就会给你n堂课。但是该系统不允许在星期i和星期i+1的时候选第i堂课,也不允许你在星期n和星期一的时候选第n堂课。然后连你自己也搞不清哪种选课方案合法,哪种选课不合法了。你只想知道,你到底有多少种合法的选课方案。
Solution
这题用容斥原理来搞,
设
Wk
表示至少有k个位置是错位的方案数,
我们先选i个一定是错位的,再乘以剩下的所有排列方案即可,
首先来给容斥没有学好的孩纸们讲一下为什么是容斥:
以选至少两个错位时的来举例子,(红色的是先选的错位的,棕色的是另外选时选的,稍微红一点的是另外选时错位的)
而这种情况也会出现:
但我们发现这其实是一样的,所以要减掉
W3
,但又会减重,所以又要加回
W4
……,
所以是用容斥,
现在对于每个
Wk=(n−k)!∗wk,wk
为选k个一定错位的方案数,
对于每天,对应的错误的课为:
(1,2),(2,3),.....,(n,1)
,
拆开来就是:
1,1,2,2,3,3,....,n,n
,
又因为拆开以后选的每一节课都一定可以对应一天,所以只要在这堆数中选即可,
现在要求的就是这
2n
个数围成的圆中(第一个,最后一个不能被同时选),选i个两两不相邻的数方案数(一节课不可以被选两次嘛),
这里引用LYD的解释方法:
设黑色为选,白色不选:
我们让黑色吃掉它右边一个,最后一个不吃,就变成了(被吃的为灰色):
同样的,从下图也可以推出一个唯一的上图,
当然,当第一个和最后一个同时选的情况时不合法的要减掉,
所以就是
化解一下:
于是就可以求出 Wi ,之后再用容斥搞一波,
复杂度: O(n∗T),T 为数据组数,
其实还有一种奇怪的递推方法,我不会!
Code
注意常数对程序带来的影响!!!
本题需卡常!!!
当然你打表也没人拦着你~
#include<cstdio>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long LL;
const int N=200050,mo=1e9+7;
LL n,ans;
LL jc[N],jcn[N],ny[N];
LL ksm(LL q,int w)
{
LL ans=1;q%=mo;
while(w)
{
if(w&1)(ans*=q)%=mo;
(q*=q)%=mo;w>>=1;
}
return ans;
}
LL C(int i,int j){return jc[j]*jcn[i]%mo*jcn[j-i]%mo;}
int main()
{
jc[0]=jcn[0]=1;
fo(i,1,N-2)jc[i]=(jc[i-1]*i)%mo,jcn[i]=ksm(jc[i],mo-2),ny[i]=ksm(i,mo-2);
while(scanf("%lld",&n)!=EOF)
{
ans=0;
if(n>1)fo(k,0,n)
if(k&1)ans=(ans-2*n*ny[2*n-k]%mo*C(k,2*n-k)%mo*jc[n-k])%mo;
else ans=(ans+2*n*ny[2*n-k]%mo*C(k,2*n-k)%mo*jc[n-k])%mo;
printf("%lld\n",(ans+mo)%mo);
}
return 0;
}