[CF340E]Iahub and Permutations

280 篇文章 1 订阅
220 篇文章 2 订阅

题目

传送门 to luogu

思路

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

目前给出了图的一部分。假设此处的 n n n 只包含“闲着”的点。链考虑继续往后接。若接了 k k k 个数,那么考虑先将 k k k 全排,再用隔板法将其割成 c n t cnt cnt 截。此处 c n t cnt 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} (kn)k!(cnt1k+cnt1)

然而 W A \tt WA WA 了,何故?因为两条链还可以接起来。那么剩下的情况是将这 c n t cnt cnt 条链连接成任意个圆排列。答案是 c n t ! cnt! cnt! 。剩下的点就是经典错拍。记之为 d ( x ) 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! ans=k=0n(kn)(cnt1k+cnt1)d(nk)k!cnt!

复杂度 O ( n ) \mathcal O(n) 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 dp 做法,其转化值得参考。不过递推式化简到最后就是容斥做法

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值