6830. 【2020.10.25提高组模拟】排列

题目

https://gmoj.net/senior/#main/show/6830

题解

发现原题所求等价于现在我们可以把一个序列的头或尾扔进序列中的任意一个位置上,对于每一个 [ 0 , n − 1 ] [0,n-1] [0,n1]中的 k k k,求有多少个序列在进行不超过k次操作后能够变成排列 1 , 2 , 3 , ⋯ n − 1 , n 1,2,3,\cdots n-1,n 1,2,3,n1,n

容易发现这样的序列要满足一个条件:最长连续上升子序列的长度大于等于 n − k n-k nk

正难则反,令 p = n − k p=n-k p=nk,现在要求的是有多少个 n n n的排列使得其最长连续上升子序列长度小于 p p p,也就是所有连续上升子序列的长度都小于 p p p

那么现在有一个很简单的想法:
f i f_i fi表示当前已经处理完序列的前 i i i个位置的序列数。那么 f i = ∑ j = i − p + 1 i − 1 f j ⋅ ( n − j i − j ) f_i=\sum_{j=i-p+1}^{i-1}f_{j}\cdot \tbinom{n-j}{i-j} fi=j=ip+1i1fj(ijnj)

但是一个长度为 l e n len len的连续上升子序列可能会被算了多次(比如序列 2 , 3 , 5 , 6 , 9 2,3,5,6,9 2,3,5,6,9会被 2 , 3 2,3 2,3 5 , 6 , 9 5,6,9 5,6,9重复计算),假设长度为 l e n len len的序列被计算了 h l e n h_{len} hlen次, l e n len len有一种划分方案使得 l e n = ∑ a i len=\sum a_i len=ai,那么 h l e n = ∑ 每一种划分方案 a ∏ h a i h_{len}=\sum_{\text{每一种划分方案}a}\prod h_{a_i} hlen=每一种划分方案ahai
因此我们还需要在 f j ⋅ ( n − j i − j ) f_j\cdot \tbinom{n-j}{i-j} fj(ijnj)后乘上一个容斥系数 g i − j g_{i-j} gij
现在就要现办法使得乘上这个容斥系数后,每一个长度的序列只被计算一次。

现在要解出 g g g,于是把 g g g写成生成函数的形式:令 G ( x ) = ∑ i = 0 n g i x i G(x)=\sum_{i=0}^n g_ix^i G(x)=i=0ngixi
那么有 ∑ i = 0 + ∞ G ( x ) i = ∑ i = 0 p − 1 x i \sum_{i=0}^{+\infty}G(x)^i=\sum_{i=0}^{p-1}x^i i=0+G(x)i=i=0p1xi
这里为什么会有一段 ∑ i = 0 + ∞ \sum_{i=0}^{+\infty} i=0+呢?
首先, G ( x ) i G(x)^i G(x)i的意义是把段分为 i i i块。而如果用一个非无限大的数代替 + ∞ +\infty +的话,会出现一个无法消去的最高次项。因此是这样。

代入等比数列求和公式,得
− 1 G ( x ) − 1 = x p − 1 x − 1 1 − G ( x ) = x − 1 x p − 1 G ( x ) = x p − x x p − 1 G ( x ) = 1 + 1 x p − 1 − x ⋅ 1 x p − 1 G ( x ) = ∑ i = 0 + ∞ x p i + 1 − ∑ i = 1 + ∞ x p i \begin{aligned} \cfrac{-1}{G(x)-1}&=\cfrac{x^p-1}{x-1}\\[2ex] 1-G(x)&=\cfrac{x-1}{x^p-1}\\[2ex] G(x)&=\cfrac{x^p-x}{x^p-1}\\[2ex] G(x)&=1+\cfrac{1}{x^p-1}-x\cdot \cfrac{1}{x^p-1}\\[2ex] G(x)&=\sum_{i=0}^{+\infty} x^{pi+1}-\sum_{i=1}^{+\infty} x^{pi} \end{aligned} G(x)111G(x)G(x)G(x)G(x)=x1xp1=xp1x1=xp1xpx=1+xp11xxp11=i=0+xpi+1i=1+xpi
因此, g i = { 1 , i ≡ 1 ( m o d n − k + 1 ) − 1 , i ≡ 0 ( m o d n − k + 1 ) 0 , i ≢ 1 或 0 g_i=\begin{cases} 1,& i\equiv 1\pmod{n-k+1}\\[2ex] -1,& i\equiv 0\pmod{n-k+1}\\[2ex] 0,& i\not\equiv1\text{或}0 \end{cases} gi=1,1,0,i1(modnk+1)i0(modnk+1)i10
然后DP就好了。因为有值的 g g g只有 n k \cfrac{n}{k} kn个,所以直接暴力转移就可以了。时间复杂度 O ( n 2 log ⁡ 2 n ) O(n^2\log_2 n) O(n2log2n)

CODE

#include<cstdio>
#include<cstring>
using namespace std;
#define N 1005
int P,inv[N],fac[N],f[N],ans[N];
inline int C(int x,int y){return 1LL*fac[x]*inv[y]%P*inv[x-y]%P;}
inline void add(int &x,int y){x+=y;if(x>=P) x-=P;}
inline void minus(int &x,int y){x-=y;if(x<0) x+=P;}
int main()
{
	freopen("permutation.in","r",stdin);
	freopen("permutation.out","w",stdout);
	int n,k,p;
	scanf("%d%d",&n,&P);
	fac[0]=1;for(int i=1;i<N;++i) fac[i]=1LL*fac[i-1]*i%P;
	inv[1]=1;for(int i=2;i<N;++i) inv[i]=1LL*inv[P%i]*(P-P/i)%P;
	inv[0]=1;for(int i=1;i<N;++i) inv[i]=1LL*inv[i-1]*inv[i]%P;
	ans[0]=fac[n];
	for(int k=1;k<n;++k)//这里枚举的是上文中的n-k,p=n-k+1.
	{
		p=k+1,memset(f,0,sizeof f),f[0]=1;
		for(int i=0;i<n;++i) if(f[i])
		{
			for(int j=1;i+j<=n;j+=p) add(f[i+j],1LL*f[i]*C(n-i,j)%P);
			for(int j=p;i+j<=n;j+=p) minus(f[i+j],1LL*f[i]*C(n-i,j)%P);
		}
		ans[k]=fac[n]-f[n];
		if(ans[k]<0) ans[k]+=P;
	}
	for(int i=0;i<n;++i) printf("%d\n",ans[n-i-1]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值