【题解】CF1542E2 Abnormal Permutation Pairs (hard version)
数数好题。
求二元组 \((p,q)\) 的数量使得 \(p,q\) 是长度为 \(n\) 排列,其中 \(p\) 的字典序更小,逆序对更多。
需要比字典序,我们枚举第一个不相同的的位置 \(i\) 。那么 \(i\) 前面的方案是 \(i!\binom{n}{i}\)。
我们枚举第 \(i\) 位上 \(p,q\) 分别填的数是 \(l,r\) ,那么记对应后面的方案位 \(F_{n-i,r-l+1}\) 。
不难发现,我们要求的 \(F_{i,j}\) 就是二元组 \((u,v)\) 的数量,使得 \(u,v\) 是长度为 \(i\) 的排列,其中 \(u\) 的逆序对数比 \(v\) 的多 \(j\) 个。
这玩意看起来就比较阴间,我们转化为求 \(f_{i,j}\) 表示逆序对恰好多 \(j\) 个。
我们可以直接计算 \(g_{i,j}\) 表示长度为 \(i\),逆序对数为 \(j\) 的排列数,然后利用 \(g\) 来计算 \(f\),这样做时间复杂度是 \(\mathcal{O}(N^5)\),前缀和优化以下可以做到 \(\mathcal{O}(N^4)\) ,并且我手算了以下发现再计算一次前缀和并用卷积优化可以做到 \(\mathcal{O}(N^3\log N)\) 。
以上的复杂度仍然不足以通过。
由于计算 \(g\) 的复杂度已经到了极限,我们考虑直接计算 \(f\) ,不难发现枚举第 \(i\) 位上填的两个数,我们关心的只有这两个数的差。直接作差可以优化一维,时间复杂度是 \(\mathcal{O}(N^4)\),直接卷积可以做到 \(\mathcal{O}(N^3\log N)\) 。
再观察一下,发现我们求得大致是形如 \(f_{i,j}=\sum |j-k|\times f_{i-1,k}\) ,拆掉绝对值后,可以将贡献拆为 \(\sum\limits_{j} f_{i,j}\) 和 \(\sum \limits_{j}j\times f_{i,j}\) 两部分(篇幅原因省略了系数),这两部分都可以通过前缀和求出来,所以直接前缀和优化可以做到 \(\mathcal{O}(N^3)\) 。
直接开空间开不下,需要滚动数组优化。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 1005
using namespace std;
int n,P,f[N * N * 2],g[N * N * 2],p[N * N * 2],c[N][N],fac[N],ss,ans;
inline void ad(int &x,int y){x=(x+y)%P;if(x<0)x+=P;}
void maintain(int lim){
g[ss - lim] = f[ss - lim];
p[ss - lim] = - lim * f[0] % P + P;
rep(j,-lim + 1,lim)
p[j + ss] = (p[j - 1 + ss] + 1LL * f[j + ss] * j % P + P) % P,
g[j + ss] = (g[j - 1 + ss] + f[j + ss]) % P;
}
int get(int *u,int l,int r){
if(l > r)return 0;
return (u[r + ss] - u[l - 1 + ss] + P) % P;
}
void calc(){
f[ss] = 1;maintain(0);
rep(i, 1, n - 1){
int cur = i * (i - 1) / 2;
rep(k, -cur , cur){
int L = max(k - i + 1, -cur);
int R = min(k + i - 1, cur);
f[k + ss] = 1LL * i * get(g, L, R) % P;
ad(f[k + ss], (1LL * get(g, k + 1, R) * k - get(p, k + 1, R)) % P);
ad(f[k + ss], (get(p, L, k) - 1LL * get(g, L, k) * k % P) % P);
}
maintain(i * (i + 1) / 2);
int w = n - i;
int sum = n - w + 1;
int ins = 1LL * c[n][w - 1] * fac[w - 1] % P;
rep(l, 1, sum)rep(r, l + 1, sum){
int k = r - l + 1;
ans = (ans + 1LL * ins * get(g, k, cur)) % P;
}
}
}
signed main(){
scanf("%d%d",&n,&P);ss = n * (n - 1)/2;
fac[0] =1;rep(i,1,n)fac[i]=1LL*fac[i-1]*i%P;
rep(i,0,n){
c[i][0]=1;
rep(j,1,i)c[i][j] = (c[i - 1][j - 1] + c[i - 1][j])%P;
}
calc();printf("%d\n",ans);
return 0;
}