CF1542E Abnormal Permutation Pairs
题意
给定一个 \(n\),求计数长度为 \(n\) 的排列 \(p,q\) 使得 \(p\) 字典序小于 \(q\) 并且 \(p\) 逆序对个数大于 \(q\)。
Solution
感觉一直不太会这种排列的计数题。
对于不考虑字典序的情况,我们可以令 \(dp_{i,j}\) 表示前 \(i\) 个数构成排列有 \(j\) 个逆序对的方案数。这东西可以通过前缀和优化做到 \(O(n^3)\)。
现在考虑字典序怎么限制。先转化一手题意,字典序小,意味着 \(p\) 从前往后第一个和 \(q\) 不一样的位置 \(i\),要满足 \(p_i<q_i\)。所以套路地枚举相等前缀的长度,然后考虑这段前缀的逆序对数,以及前缀与后缀产生的逆序对数,\(p,q\) 应该是相等的,所以我们就是希望能得到这段后缀中的逆序对数有 \(p>q\)。
令 \(f_i\) 表示长度为 \(i\) 的排列在题目限制中的排列对数。
如果 \(p_1=q_1\),相当于在 \(i\) 个数中选一个放到最前面来,然后要求剩下的满足限制,注意到剩下的数离散后相当于是 \(i-1\) 的排列,所以有 \(f_i=i\times f_{i-1}\)。
如果 \(p_1<q_1\),可以知道 \(p_1\) 与后面产生的逆序对数量是 \(p_1-1\),也可以知道 \(q_1\) 与后面产生的逆序对数量是 \(q_1-1\)。于是我们枚举 \(x,y\),表示的就是后面产生的逆序对个数。这样需要满足 \(x+p_1-1>y+q_1-1\)。能够知道当前 \(p_1,q_1\) 的方案数是:
然后再用上面那个 \(dp\) 直接求出逆序对数分别是 \(x,y\) 的方案数:
现在考虑一手复杂度,预处理 \(O(n^3)\),求 \(f\) 是 \(O(n^5)\),可以通过 easy version。
枚举 \(x,y\) 实在是太难做了,只枚举 \(x\),然后枚举 \(p_1,q_1\),求出 \(dp\) 的前缀和 \(pre\),这样式子就变成了:
发现后面这个东西应该只和 \(q_1-p_1\) 有关,我们令这个差值为 \(d\),则有:
令:
那么上式有:
现在需要快速求出 \(sum1,sum2\)。先求 \(sum1\),\(sum1_{i,0\sim i}\) 可以暴力做,此后递推:
求 \(sum2\),同样 \(sum2_{i,0\sim i}\) 可以暴力做,此后递推:
Code
// Problem:
// Abnormal Permutation Pairs (hard version)
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF1542E2
// Memory Limit: 2700 MB
// Time Limit: 500000 ms
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
#define int long long
using namespace std;
const int MAXN=510;
int dp[2][MAXN*MAXN],pre[2][MAXN*MAXN],MOD;
int sum1[2][MAXN*MAXN],sum2[2][MAXN*MAXN];
int C2(int i){return i*(i-1)/2;}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,ans=0;cin>>n>>MOD;
pre[1][0]=1;
sum1[1][0]=sum2[1][0]=1;
rep(i,2,n){
memset(dp[i&1],0,sizeof(dp[i&1]));
memset(pre[i&1],0,sizeof(pre[i&1]));
memset(sum1[i&1],0,sizeof(sum1[i&1]));
memset(sum2[i&1],0,sizeof(sum2[i&1]));
rep(j,0,C2(i)){
dp[i&1][j]=pre[(i-1)&1][min(C2(i-1),j)];
if(j>=i) (dp[i&1][j]+=MOD-pre[(i-1)&1][min(C2(i-1),j-i)])%=MOD;
if(j) pre[i&1][j]=pre[i&1][j-1];
(pre[i&1][j]+=dp[i&1][j])%=MOD;
}
rep(x,0,min(C2(i),i)) rep(d,1,min(i,x))
(sum1[i&1][x]+=pre[i&1][x-d])%=MOD,
(sum2[i&1][x]+=d*pre[i&1][x-d]%MOD)%=MOD;
rep(x,min(C2(i),i)+1,C2(i))
sum1[i&1][x]=(sum1[i&1][x-1]+MOD-pre[i&1][x-1-i]+pre[i&1][x-1])%MOD,
sum2[i&1][x]=(sum2[i&1][x-1]+sum1[i&1][x-1]+MOD-(i+1)*pre[i&1][x-1-i]%MOD+pre[i&1][x-1])%MOD;
// rep(x,0,C2(i)) rep(d,1,min(i,x))
// (sum1[i&1][x]+=pre[i&1][x-d])%=MOD,
// (sum2[i&1][x]+=d*pre[i&1][x-d]%MOD)%=MOD;
ans=ans*i%MOD;
rep(x,1,C2(i-1))
ans=(ans+dp[(i-1)&1][x]*(i*sum1[(i-1)&1][x-1]%MOD+MOD-sum2[(i-1)&1][x-1])%MOD)%MOD;
}cout<<ans<<'\n';
return 0;
}