[CF340E]Iahub and Permutations

题目

传送门 to luogu

思路

显然错排的含义是,圆排列,大小至少为二。

目前给出了图的一部分。假设此处的 n n 只包含“闲着”的点。链考虑继续往后接。若接了 k k 个数,那么考虑先将 k k 全排,再用隔板法将其割成 c n t cnt 截。此处 c n t cnt 指链的数量。所以是

( n k ) k ! ( k + c n t 1 c n t 1 ) {n\choose k}\cdot k!\cdot{k+cnt-1\choose cnt-1}

然而 W A \tt WA 了,何故?因为两条链还可以接起来。那么剩下的情况是将这 c n t cnt 条链连接成任意个圆排列。答案是 c n t ! cnt! 。剩下的点就是经典错拍。记之为 d ( x ) d(x) ,则

a n s = k = 0 n ( n k ) ( k + c n t 1 c n t 1 ) d ( n k )    k !    c n t ! ans=\sum_{k=0}^{n}{n\choose k}{k+cnt-1\choose cnt-1}d(n-k)\;k!\;cnt!

复杂度 O ( n ) \mathcal O(n) 即可解决。

代码

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int_;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(int x){
	if(x > 9) writeint(x/10);
	putchar((x%10)^48);
}

const int Mod = 1e9+7;
const int MaxN = 4005;
int jc[MaxN], inv[MaxN];
void prepare(){
	jc[1] = inv[1] = 1;
	for(int i=2; i<MaxN; ++i){
		jc[i] = 1ll*jc[i-1]*i%Mod;
		inv[i] = (0ll+Mod-Mod/i)*inv[Mod%i]%Mod;
	}
	for(int i=2; i<MaxN; ++i)
		inv[i] = 1ll*inv[i-1]*inv[i]%Mod;
	jc[0] = inv[0] = 1;
}
inline int_ C(int n,int m){
	if(n == m) return 1;
	if(m < 0 || n < m) return 0;
	return 1ll*jc[n]*inv[m]%Mod*inv[n-m]%Mod;
}

bool vis[MaxN]; // 是否有入度
int a[MaxN];
int d[MaxN]; // 错排数量

int main(){
	prepare();
	int n = readint();
	d[0] = 1, d[1] = 0;
	for(int i=1; i<=n; ++i){
		a[i] = readint();
		if(~a[i]) vis[a[i]] = true;
		d[i+1] = 1ll*i*(d[i]+d[i-1])%Mod;
	}
	int cnt = 0, left = 0;
	for(int i=1; i<=n; ++i){
		if(a[i] != -1) continue;
		if(vis[i]) ++ cnt; // 链尾
		else ++ left; // 未曾出现
	}
	int ans = 0, zxy = 1;
	for(int i=2; i<=cnt; ++i)
		zxy = 1ll*zxy*i%Mod;
	for(int i=0; i<=left; ++i){
		int_ tmp = C(left,i)*zxy%Mod;
		tmp = tmp*C(i+cnt-1,cnt-1)%Mod;
		ans = (ans+tmp*d[left-i])%Mod;
		zxy = 1ll*zxy*(i+1)%Mod;
	}
	printf("%d\n",ans);
	return 0;
}

后记

也看到了 d p \tt dp 做法,其转化值得参考。不过递推式化简到最后就是容斥做法

很快很简洁的 d p \tt dp 做法当然也是有的。

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/108500064