【LOJ3395】【2020-2021 集训队作业】Yet Another Permutation Problem(容斥,生成函数,多项式)

题目相当于让我们考虑某种排列最少能经过几次操作得到。

操作是从 1 ∼ n 1\sim n 1n 的顺序排列中抽取若干个元素放到排列头和尾,那么假设进行了 l l l 次操作, 1 ∼ n 1\sim n 1n 中还会剩下至少 n − l n-l nl 个元素没有被抽取,也就是说这个排列的最长连续上升段长度至少是 n − l n-l nl

也就是说经过 l l l 次操作能得到某个排列 p p p 的必要条件是 p p p 的最长连续上升段的长度至少是 n − l n-l nl

显然这个条件也是充要条件:假设某个排列 p p p 的最长连续上升段的长度大于等于 n − l n-l nl,那么我们可以通过至多 l l l 次操作就能逆推构造出操作的方法。

经过 l l l 次操作能得到某个排列 p p p 的充要条件是 p p p 的最长连续上升段的长度至少是 n − l n-l nl

那么题目的问题就是对于每一个 k = 0 ∼ n − 1 k=0\sim n-1 k=0n1 问有多少个排列的最长连续上升段的长度至少是 n − k n-k nk

也等价于存在某个连续上升段长度为 n − k n-k nk

反过来会好计算一点:考虑有多少个排列满足该排列的每个连续上升段都小于 n − k n-k nk,最后用 n ! n! n! 减去即可。

以下为了方便,令 k ← n − k − 1 k\gets n-k-1 knk1

我们枚举 T = { [ l i , r i ] } T=\{[l_i,r_i]\} T={[li,ri]} 是排列的连续上升段的一种划分方式(即将排列划分为若干个区间 [ l i , r i ] [l_i,r_i] [li,ri],并且钦定所有的 [ l i , r i ] [l_i,r_i] [li,ri] 段内数字上升,但注意 [ l i , r i ] [l_i,r_i] [li,ri] 可能并不是一个极长的连续上升段),大胆假设:
A n s = ∑ T ( ∏ [ l , r ] ∈ T f ( r − l + 1 ) ) ( n ! ∏ [ l , r ] ∈ T ( r − l + 1 ) ! ) Ans=\sum_{T}\left(\prod_{[l,r]\in T}f(r-l+1)\right)\left(\dfrac{n!}{\prod\limits_{[l,r]\in T}(r-l+1)!}\right) Ans=T[l,r]Tf(rl+1)[l,r]T(rl+1)!n!
其中 ( ∏ [ l , r ] ∈ T f ( r − l + 1 ) ) \left(\prod\limits_{[l,r]\in T}f(r-l+1)\right) ([l,r]Tf(rl+1)) 是容斥系数, ( n ! ∏ [ l , r ] ∈ T ( r − l + 1 ) ! ) \left(\dfrac{n!}{\prod\limits_{[l,r]\in T}(r-l+1)!}\right) [l,r]T(rl+1)!n! 是将 1 ∼ n 1\sim n 1n 分配给这些连续上升段的方案数。

那么接下来,我们需要构造一个容斥系数 f f f 使得上式成立。

考虑 1 ∼ n 1\sim n 1n 的一种排列 p p p,将它划分为若干个极长的连续上升段 T = { [ l i , r i ] } T=\{[l_i,r_i]\} T={[li,ri]}

考虑这种排列在式子左右两边的贡献。它在等号左边的贡献是 [ max ⁡ [ l , r ] ∈ T ( r − l + 1 ) ≤ k ] [\max_{[l,r]\in T}(r-l+1)\leq k] [max[l,r]T(rl+1)k]

考虑它在等号右边的贡献:对于任意的 [ l , r ] ∈ T [l,r]\in T [l,r]T,它都有可能被分为若干个小段并被统计。

我们枚举将 [ l , r ] [l,r] [l,r] 划分为若干个小段 K [ l , r ] = { [ l i ′ , r i ′ ] } K_{[l,r]}=\{[l'_i,r'_i]\} K[l,r]={[li,ri]},那么 T T T 在等号右边的贡献是:
∏ [ l , r ] ∈ T ∑ K [ l , r ] ∏ [ l ′ , r ′ ] ∈ K [ l , r ] f ( r ′ − l ′ + 1 ) \prod_{[l,r]\in T}\sum_{K_{[l,r]}}\prod_{[l',r']\in K_{[l,r]}}f(r'-l'+1) [l,r]TK[l,r][l,r]K[l,r]f(rl+1)

又发现贡献只和段长 r ′ − l ′ + 1 r'-l'+1 rl+1 有关,与 l ′ , r ′ l',r' l,r 无关,那么就相当于把 r − l + 1 r-l+1 rl+1 分解成若干个数的和。于是如果我们设 F ( x ) F(x) F(x) f f f 的 OGF,即 F ( x ) = ∑ i f ( i ) x i F(x)=\sum_i f(i)x^i F(x)=if(i)xi,那么:
[ max ⁡ [ l , r ] ∈ T ( r − l + 1 ) ≤ k ] = ∏ [ l , r ] ∈ T [ x r − l + 1 ] ( ∑ i F ( x ) i ) ∏ [ l , r ] ∈ T [ r − l + 1 ≤ k ] = ∏ [ l , r ] ∈ T [ x r − l + 1 ] 1 1 − F ( x ) \begin{aligned} \big[\max_{[l,r]\in T}(r-l+1)\leq k\big]=\prod_{[l,r]\in T}[x^{r-l+1}]\left(\sum_{i} F(x)^i\right)\\ \prod_{[l,r]\in T}[r-l+1\leq k]=\prod_{[l,r]\in T}[x^{r-l+1}]\dfrac{1}{1-F(x)} \end{aligned} [[l,r]Tmax(rl+1)k]=[l,r]T[xrl+1](iF(x)i)[l,r]T[rl+1k]=[l,r]T[xrl+1]1F(x)1
满足上式的一个充分条件是:
[ r − l + 1 ≤ k ] = [ x r − l + 1 ] 1 1 − F ( x ) 1 1 − F ( x ) = ∑ i = 0 k x i = 1 − x k + 1 1 − x F ( x ) = x − x k + 1 1 − x k + 1 \begin{aligned} \big[r-l+1\leq k\big]&=[x^{r-l+1}]\dfrac{1}{1-F(x)}\\ \dfrac{1}{1-F(x)}&=\sum_{i=0}^kx^i=\dfrac{1-x^{k+1}}{1-x}\\ F(x)&=\dfrac{x-x^{k+1}}{1-x^{k+1}} \end{aligned} [rl+1k]1F(x)1F(x)=[xrl+1]1F(x)1=i=0kxi=1x1xk+1=1xk+1xxk+1
于是对于每一个 k k k,可以使用多项式求逆 O ( n log ⁡ n ) O(n\log n) O(nlogn) 算出所有的 f f f

然后再看看答案怎么推:
A n s = ∑ T ( ∏ [ l , r ] ∈ T f ( r − l + 1 ) ) ( n ! ∏ [ l , r ] ∈ T ( r − l + 1 ) ! ) = n ! ∑ T ∏ [ l , r ] ∈ T f ( r − l + 1 ) ( r − l + 1 ) ! \begin{aligned} Ans&=\sum_{T}\left(\prod_{[l,r]\in T}f(r-l+1)\right)\left(\dfrac{n!}{\prod\limits_{[l,r]\in T}(r-l+1)!}\right)\\ &=n!\sum_T\prod_{[l,r]\in T}\dfrac{f(r-l+1)}{(r-l+1)!} \end{aligned} Ans=T[l,r]Tf(rl+1)[l,r]T(rl+1)!n!=n!T[l,r]T(rl+1)!f(rl+1)
那么如果设 G ( x ) G(x) G(x) f f f 的 EGF,就有:
A n s = n ! [ x n ] ( ∑ i G ( x ) i ) = n ! [ x n ] 1 1 − G ( x ) \begin{aligned} Ans&=n![x^n]\left(\sum_{i} G(x)^i\right)\\ &=n![x^n]\dfrac{1}{1-G(x)} \end{aligned} Ans=n![xn](iG(x)i)=n![xn]1G(x)1
于是求出 f f f 后也能用多项式求逆 O ( n log ⁡ n ) O(n\log n) O(nlogn) 求。总时间复杂度 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn)

然后还有一种做法:

注意到 1 1 − x k + 1 \frac{1}{1-x^{k+1}} 1xk+11 只有 n k + 1 \frac{n}{k+1} k+1n 项非零,乘上 x − x k + 1 x-x^{k+1} xxk+1 之后也只有 2 n k + 1 2\frac{n}{k+1} 2k+1n 项非零,即 F ( x ) F(x) F(x) 只有 2 n k + 1 2\frac{n}{k+1} 2k+1n 非零,于是 G ( x ) G(x) G(x) 1 − G ( x ) 1-G(x) 1G(x) 都只有 2 n k + 1 2\frac{n}{k+1} 2k+1n 项非零,于是暴力求 1 1 − G ( x ) \frac{1}{1-G(x)} 1G(x)1 的复杂度为 O ( n 2 k ) O(\frac{n^2}{k}) O(kn2),那么总复杂度为 O ( ∑ k = 1 n n 2 k ) = O ( n 2 log ⁡ n ) O(\sum_{k=1}^n\frac{n^2}{k})=O(n^2\log n) O(k=1nkn2)=O(n2logn)。那么就不用多项式求逆了。

再往下手推一下的话代码可以更加简洁,但懒得推了,就这样吧:

#include<bits/stdc++.h>

#define N 1010

using namespace std;

namespace modular
{
	int mod;
	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
	inline int mul(int x,int y){return 1ll*x*y%mod;}
	inline void Add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
	inline void Dec(int &x,int y){x=x-y<0?x-y+mod:x-y;}
}using namespace modular;

inline int poww(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1) ans=mul(ans,a);
		a=mul(a,a);
		b>>=1;
	}
	return ans;
}

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<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n;
int fac[N],ifac[N];
int F[N],GG[N],Q[N],R[N];
//GG:1-G,Q=1/GG,R=1%GG

vector<int>v;

int main()
{
	n=read(),mod=read();
	fac[0]=1;
	for(int i=1;i<=n;i++) fac[i]=mul(fac[i-1],i);
	ifac[n]=poww(fac[n],mod-2);
	for(int i=n;i>=1;i--) ifac[i-1]=mul(ifac[i],i);
	for(int k=n;k>=1;k--)
	{
		for(int i=0;i<=n;i++) F[i]=GG[i]=Q[i]=R[i]=0;
		for(int i=0;i<=n;i+=k)
		{
			if(i+1<=n) Add(F[i+1],1);
			if(i+k<=n) Dec(F[i+k],1);
		}
		v.clear();
		Add(GG[0],1);
		for(int i=0;i<=n;i++)
		{
			Add(GG[i],dec(0,mul(F[i],ifac[i])));
			if(GG[i]) v.push_back(i);
		}
		R[0]=1;
		for(int i=0;i<=n;i++)
		{
			if(R[i])
			{
				Q[i]=R[i];
				for(int j:v)
				{
					if(i+j>n) break;
					Dec(R[i+j],mul(Q[i],GG[j]));
				}
			}
		}
		printf("%d\n",dec(fac[n],mul(fac[n],Q[n])));
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值