洛谷 P4071 [SDOI2016]排列计数

简化版题意:
1~n n个数字,问满足m个ai=i的排列个数
答案对1e9+7取模

这题就是道裸题,不知道为啥还能是蓝的
前置技能一:快速幂(太简单了不讲了 这周和矩阵的知识点一起写)
前置技能二:错排公式(顾名思义错排就是ai!=i的排列个数,高中应该都学过)
下面是推理过程:我们设f[n]代表n个数的错排结果,我们考虑从n-1个变到n的过程:
对于来的第n个数,我们把它放到第i个位置,那么i就有两种情况:

  1. i放到n,此时n-2个数有f[n-2]种情况
  2. i不放到n,此时我们可以把i看成n,那么剩下这n-1个数有f[n-1] 种情况
  3. i有n-1种选法,所以f[n]=(n-1)*(f[n-1]+f[n-2])

前置技能三:乘法逆元求解:这题俺用的费马小定理,其他两种这周也会写(咕咕咕警告)
这些你都会了
就完事了

#include <cstdio>
#include <iostream>
#include <algorithm>
#define int long long 
using namespace std;
const int modd=1e9+7;
int t,n,m;
int f[1000005];
int d[1000005];
int inv[1000055];
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f; 
}
int ksm(int a,int b){
    int ans=1;
	while(b>0){
		if(b&1) ans=ans*a%modd;
		a=a*a%modd;
		b>>=1; 
	} 
	return ans%modd;
}
inline int C(int x,int y){
	return (d[x]*inv[y]%modd*inv[x-y]%modd)%modd;
} 
main(){
	t=read();
 d[0]=d[1]=f[0]=f[2]=inv[0]=inv[1]=1;d[2]=2;inv[2]=ksm(d[2],modd-2);
	for(int i=3;i<=1000000;i++){
		f[i]=(i-1)*(f[i-1]+f[i-2])%modd;//错排公式 
		d[i]=d[i-1]*i%modd;
		inv[i]=ksm(d[i],modd-2)%modd;
	}
	while(t--){
		n=read();m=read();
		int ans=(C(n,m)%modd*f[n-m])%modd;
		ans=ans%modd;
		ans=ans+modd;
		ans=ans%modd;
		printf("%lld\n",ans);
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值