排列计数(permutation)

排列计数(permutation)

题目描述

 

 

求有多少种长度为n的序列A,满足以下条件:

1) 1~n这n个数在序列中各出现了一次

2) 若第i个数A[i]的值为i,则称i是稳定的。序列恰好有m个数是稳定的。

满足条件的序列可能很多,序列数对109+7取模。

 

输入

 

 

第一行一个数T,表示有T组数据。

接下来T行,每行两个整数n、m。

 

输出

 

 

输出T行,每行一个数,表示求出的序列数。

 

样例输入

5
1 0
1 1
5 2
100 50
10000 5000

样例输出

0
1
20
578028887
60695423

提示

 

【数据规模与约定】

T=500000,n≤1000000,m≤1000000。

 

来源

SDOI2016 Round1 Day2


solution

此题求C(n,m)*f[n-m](f[i]表示i个数错排的方案)

我只会容斥求错排https://blog.csdn.net/liankewei123456/article/details/81563581

这题会T

公式:

f[i]=(i-1)*(f[i-1]+f[i-2])

啥意思

考虑新加入第i个数,如果与k互换 那么方案为f[i-2]

如果不是则为f[i-1]

这样的k有(i-1)个

注意 f[0]=1,f[1]=0,f[2]=1;

剩下的就是基础啦

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 1000005
#define mod 1000000007
using namespace std;
long long T,n,m,h[maxn],ny[maxn];
long long f[maxn]; 
long long lian(long long k,int num){
	long long a=k,ans=1;
	while(num>0){
		if(num&1)ans=ans*a;
		a=a*a;a%=mod;ans%=mod;num>>=1;
	}
	return ans;
}
long long C(int n,int m){
	return (((h[n]*ny[m])%mod)*ny[n-m])%mod;
}
int main(){
	h[0]=1;
	n=1000000;
	for(int i=1;i<=n;i++){h[i]=h[i-1]*i;h[i]%=mod;}
    ny[n]=lian(h[n],mod-2);ny[0]=1;
	for(int i=n-1;i>=1;i--){
		ny[i]=ny[i+1]*(i+1);
		ny[i]%=mod;
	}
	int op=1;
	f[0]=1;f[1]=0;f[2]=1;
	for(int i=3;i<=1000000;i++){
		f[i]=((i-1)*((f[i-1]+f[i-2])%mod))%mod;
		//if(i<=50)cout<<f[i]<<endl;
	}
	cin>>T;
	while(T--){
		scanf("%lld%lld",&n,&m);
		long long tmp=C(n,m)*f[n-m];
		tmp%=mod;
		printf("%lld\n",tmp);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/liankewei123456/article/details/81877893