题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4971
题意:共有N天和N个章节,每天完成一个章节,规定第i个章节不可以在第i天或者i+1天完成(第N个章节则是第N天和第1天不能),求分配能完成所有章节的方案数。
思路:
主要还是根据棋盘多项式的公式来求解:
但是这题和ZOJ3687不同,数据量N最大有100000,因此不能爆搜,需要推一下公式。
按照题意,先求禁位组成的棋盘的棋盘多项式,再用容斥。禁位组成的棋盘如下:
rk(C)可以转化成求1-2*N组成的圆环中,取k个不相邻的数的不同方案数。
假设在圆环中先选择一个数字,那么还需2*N-3个数中选择k-1不相邻数,这时候圆环变成了一条直链。
那么根据在1~N数中选择k个不相邻的数的方案数的公式:
可以得出在1~(2*N-3)数中选择k-1个不相邻的数的方案数为:,化简得:
所以求1-2*N组成的圆环中,取k个不相邻的数的不同方案数的公式为:
然后根据容斥原理,本题的公式即为:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define maxn 100010 4 long long num[maxn*2]; 5 const long long mod = 1e9+7; 6 void init() 7 { 8 num[0] = 1; num[1] = 1; 9 for(int i = 2; i < 2*maxn; i++) 10 { 11 num[i] = num[i-1]*i%mod; 12 } 13 } 14 int N; 15 long long quick_mod(long long a, long long b, long long mod) 16 { 17 long long ret = 1; 18 long long temp = a; 19 while(b) 20 { 21 if(b&1) ret = ret*temp%mod; 22 temp = temp*temp%mod; 23 b >>= 1; 24 } 25 return ret; 26 } 27 int main() 28 { 29 // freopen("in.txt", "r", stdin); 30 init(); 31 while(~scanf("%d", &N)) 32 { 33 if(N == 1 || N == 2) 34 { 35 printf("0\n"); continue; 36 } 37 long long ans = num[N]; 38 long long a, b, temp; 39 for(int k = 1; k <= N; k++) 40 { 41 a = num[2*N-k-1]*2%mod*N%mod; 42 b = k*num[k-1]%mod*num[2*N-2*k]%mod; 43 b = quick_mod(b, mod-2, mod); 44 temp = a*b%mod*num[N-k]; 45 46 if(k&1) ans = ((ans - temp)%mod + mod)%mod; 47 else ans = (ans + temp)%mod; 48 } 49 printf("%lld\n", ans); 50 } 51 return 0; 52 }
时间: 2024-10-27 06:31:42