[SDOI2016] 排列计数 (组合数学)
[SDOI2016]排列计数
题目描述
求有多少种长度为 n 的序列 A,满足以下条件:
1 ~ n 这 n 个数在序列中各出现了一次
若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的
满足条件的序列可能很多,序列数对 10^9+7109+7 取模。
输入输出格式
输入格式:
第一行一个数 T,表示有 T 组数据。
接下来 T 行,每行两个整数 n、m。
输出格式:
输出 T 行,每行一个数,表示求出的序列数
输入输出样例
输入样例#1:
5
1 0
1 1
5 2
100 50
10000 5000
输出样例#1:
0
1
20
578028887
60695423
说明
测试点 1 ~ 3: \(T = 1000,n \leq 8,m \leq 8;\)
测试点 4 ~ 6: \(T = 1000,n \leq 12,m \leq 12;\)
测试点 7 ~ 9: \(T = 1000,n \leq 100,m \leq 100;\)
测试点 10 ~ 12:\(T = 1000,n \leq 1000,m \leq 1000;\)
测试点 13 ~ 14:\(T = 500000,n \leq 1000,m \leq 1000;\)
测试点 15 ~ 20:\(T = 500000,n \leq 1000000,m \leq 1000000。\)
Solution
错排公式/组合计数 裸题
\(Ans=C_{n}^{m}\times D_{n-m}\),其中\(D_{i}\)为共i个元素的错排方案数(错排指元素i不在下标为i的位置上)
然后因为要取模,费马小定理求一下逆元
在这里摆一下组合及错排的公式
\[C_{n}^{m}=\frac{n!}{m!\times{(n-m)!}}
\]
\[D_{n}=(n-1)\times(D_{n-1}+D_{n-2})
\]
其实错排还有一个通项公式,但是由于时间复杂度太高,所以预处理不太常用,但在这里还是摆一下
\[D_{n}=n!(1-\frac{1}{1!}+\frac{1}{2!}-\frac{1}{3!}+...+(-1)^n\frac{1}{n!})
\]
Code
#include<bits/stdc++.h>
#define Min(a,b) (a)<(b)?(a):(b)
#define Max(a,b) (a)>(b)?(a):(b)
#define rg register
#define il inline
#define lol long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
void in(int &ans) {
ans=0; int f=1; char i=getchar();
while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar();
ans*=f;
}
int T,n,m;
lol D[N],ie[N],sum[N];//ie[]是逆元数组,inverse element的简称
lol qpow(lol a,int x,lol ans=1) {
while(x) {
if(x&1) ans=ans*a%mod;
x>>=1,a=a*a%mod;
}return ans;
}
il void init() {
D[0]=D[2]=1; for(rg int i=3;i<=N;i++) D[i]=(i-1)*(D[i-1]+D[i-2])%mod;
ie[0]=sum[0]=1; for(rg int i=1;i<=N;i++) sum[i]=sum[i-1]*i%mod,ie[i]=qpow(sum[i],mod-2);
}
int main()
{
in(T); init();
while(T--) {
in(n),in(m);
printf("%lld\n",D[n-m]*sum[n]%mod*ie[m]%mod*ie[n-m]%mod);
}
return 0;
}
博主蒟蒻,随意转载.但必须附上原文链接
http://www.cnblogs.com/real-l/
博主蒟蒻,随意转载.但必须附上原文链接
http://www.cnblogs.com/real-l/
http://www.cnblogs.com/real-l/