Loading

CF1542E2 题解

首先,考虑枚举其中一个的逆序对数,这里绕不开的问题就是求 \(I_{i,j}\) 表示 \(1-i\) 的排列中逆序对个数为 \(j\) 的排列数,不妨把这里逆序对变成顺序对(为了方便描述,显然是等价的)。

有个很显然的trick:把所有数按 \(1-n\) 顺序插入。然后当插入第 \(i\) 个数时,枚举它前面有 \(k\) 个数,则顺序对会增加 \(k\) 个。于是 \(I_{i,j}\leftarrow\sum\limits_{k=0}^{i-1} I_{i-1,j-k}\)

复杂度: \(n\times n^2\times n\),是 \(O(n^4)\) 的。

前缀和优化一下,记 \(SI_{i,j}=\sum\limits_{k=0}^j I_{i,j}\),那么 \(I_{i,j}\leftarrow SI_{i-1,j}-SI_{i-1,j-i}\),就可以 \(O(n^3)\) 了。

最初:\(O(n^7)\)

考虑这题,用递推(我也不知道是怎么想到的)。

\(f_i\) 表示本题长度为 \(i\) 时的答案。

考虑枚举两个排列的最后一位,分别为 \(p,q\)

\(p=q\),则 \(f_i\leftarrow i\times f_{i-1}\)

否则,枚举 \(p,q\) 和直接第一、二个排列之前的逆序对数 \(j,k\)

\(f_i\leftarrow \sum\limits_{p=1}^i\sum\limits_{q=p+1}^i\sum\limits_{j=p-1}^{p-1+(i-2)\times(i-2)/2}\sum\limits_{k=q-1}^{j-1} I_{i-1,j-(p-1)}\times I_{i-1,k-(q-1)}\)

两个加起来即可,复杂度是 \(O(n^7)\) 的,非常优秀,能过样例,显然会超时。

优化:\(O(n^5)\)

\(\sum\limits_{p=1}^i\sum\limits_{q=p+1}^i\sum\limits_{j=p-1}^{p-1+(i-2)\times(i-2)/2}\sum\limits_{k=q-1}^{j-1} I_{i-1,j-(p-1)}\times I_{i-1,k-(q-1)}=\sum\limits_{p=1}^i\sum\limits_{q=p+1}^i\sum\limits_{j=p-1}^{p-1+(i-2)\times(i-2)/2} I_{i-1,j-(p-1)}\sum\limits_{k=q-1}^{j-1} I_{i-1,k-(q-1)}\)

\(=\sum\limits_{p=1}^i\sum\limits_{q=p+1}^i\sum\limits_{j=p-1}^{p-1+(i-2)\times(i-2)/2} I_{i-1,j-(p-1)}SI_{i-1,j-q}\)

有个细节,就是 \(j-q\)\(\ge0\),所以 \(j\) 要从 \(q\) 开始枚举 \((q\ge p-1)\)

于是变成 \(\sum\limits_{p=1}^i\sum\limits_{q=p+1}^i\sum\limits_{j=q}^{p-1+(i-2)\times(i-2)/2} I_{i-1,j-(p-1)}SI_{i-1,j-q}\)

搞掉了一个 \(n^2\) ,于是变成了 \(O(n^5)\)

类似优化:\(O(n^4)\)

\(\sum\limits_{p=1}^i\sum\limits_{q=p+1}^i\sum\limits_{j=q}^{p-1+(i-2)\times(i-2)/2} I_{i-1,j-(p-1)}SI_{i-1,j-q}\) 之前推到了这个式子。

下面优化 \(q\)。考虑 \(\sum\limits_{q=p+1}^i\sum\limits_{j=q}^{p-1+(i-2)\times(i-2)/2}\),这等价于 \(\sum\limits_{j=p+1}^{p-1+(i-2)\times(i-2)/2}\sum\limits_{q=p+1}^{\min(j,i)}\)

于是 \(\sum\limits_{p=1}^i\sum\limits_{q=p+1}^i\sum\limits_{j=q}^{p-1+(i-2)\times(i-2)/2} I_{i-1,j-(p-1)}SI_{i-1,j-q}=\sum\limits_{p=1}^i\sum\limits_{j=p+1}^{p-1+(i-2)\times(i-2)/2} I_{i-1,j-(p-1)}\sum\limits_{q=p+1}^{\min(j,i)}SI_{i-1,j-q}\)

\(SSI\)\(SI\) 的前缀和,则这个式子变成 \(\sum\limits_{p=1}^i\sum\limits_{j=p+1}^{p-1+(i-2)\times(i-2)/2} I_{i-1,j-(p-1)}\times T\)

其中 \(T=\begin{cases}SSI_{j-p-1}(j\le i)\\SSI_{j-p-1}-SSI_{j-i-1}(j>i)\end{cases}\)(那篇题解这里就错了)。

终极优化:\(O(n^3)\)

都优化到这个份上了,相信大家能想到这个变成三次方的优化。

类似的 \(\sum\limits_{p=1}^i\sum\limits_{j=p+1}^{p-1+(i-2)\times(i-2)/2}I_{i-1,j-(p-1)}\times T=\sum\limits_{j=2}^{p-1+(i-2)\times(i-2)/2}\sum\limits_{p=1}^{\min(i,j-1)}I_{i-1,j-(p-1)}\times T\)

显然是把 \(I_{i-1,j-(p-1)}\times T\) 这整个去前缀和。

\(S\)\(I_{k+2}\times SSI_k\) 的前缀和,这个式子就变成了:\(\sum\limits_{j=2}^{i\times(i-1)/2} T\)

其中 \(T=\begin{cases}S_{j-2}(j\le i)\\S_{j-2}-S_{j-i-1}-SSI_{j-i-1}\times(SI_{j}-SI_{j+i-1}) \end{cases}\)

这样就是 \(O(n^3)\) 啦!


注意到三次方空间会爆,于是把枚举 \(i\) 的那一维想办法优化一下就行了。

代码:

#include<bits/stdc++.h>
#define LL long long
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const int N=505;
int n,mod,I[N*N>>1],SI[N*N>>1],SSI[N*N>>1],S[N*N>>1],f[N];
inline int AD(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline void ad(int &x,int y){x+=y;(x>=mod)&&(x-=mod);}
int main()
{
	scanf("%d%d",&n,&mod);I[0]=SI[0]=SSI[0]=1;f[0]=0;
	for(int i=1;i<=n;i++)
	{
		f[i]=1ll*f[i-1]*i%mod;
		for(int j=2;j<=i*(i-1)/2;j++)
		{
			if(j<=i) ad(f[i],S[j-2]);
			else ad(f[i],AD(S[j-2],mod-AD(S[j-i-1],1ll*SSI[j-i-1]*AD(SI[j],mod-SI[j-i+1])%mod)));
		}
		for(int j=0;j<=i*(i-1)/2;j++)
		{
			if(j>=i) I[j]=AD(SI[j],mod-SI[j-i]);
			else I[j]=SI[j];
		}
		SI[0]=SSI[0]=I[0];S[0]=1ll*I[2]*SSI[0]%mod;
		for(int j=1;j<=i*(i+1)/2;j++) SI[j]=AD(SI[j-1],I[j]),SSI[j]=AD(SSI[j-1],SI[j]),S[j]=AD(S[j-1],1ll*I[j+2]*SSI[j]%mod);
	}
	printf("%d",f[n]);
	return 0;
}

个人感觉实现的比较优秀,从七次方优化到三次方的 \(dp\) 还是很精妙的。

posted @ 2023-05-11 23:29  HaHeHyt  阅读(37)  评论(0编辑  收藏  举报