CF1542E Abnormal Permutation Pairs
题意#
给定一个 ,求计数长度为 的排列 使得 字典序小于 并且 逆序对个数大于 。
Solution#
感觉一直不太会这种排列的计数题。
对于不考虑字典序的情况,我们可以令 表示前 个数构成排列有 个逆序对的方案数。这东西可以通过前缀和优化做到 。
现在考虑字典序怎么限制。先转化一手题意,字典序小,意味着 从前往后第一个和 不一样的位置 ,要满足 。所以套路地枚举相等前缀的长度,然后考虑这段前缀的逆序对数,以及前缀与后缀产生的逆序对数, 应该是相等的,所以我们就是希望能得到这段后缀中的逆序对数有 。
令 表示长度为 的排列在题目限制中的排列对数。
如果 ,相当于在 个数中选一个放到最前面来,然后要求剩下的满足限制,注意到剩下的数离散后相当于是 的排列,所以有 。
如果 ,可以知道 与后面产生的逆序对数量是 ,也可以知道 与后面产生的逆序对数量是 。于是我们枚举 ,表示的就是后面产生的逆序对个数。这样需要满足 。能够知道当前 的方案数是:
然后再用上面那个 直接求出逆序对数分别是 的方案数:
现在考虑一手复杂度,预处理 ,求 是 ,可以通过 easy version。
枚举 实在是太难做了,只枚举 ,然后枚举 ,求出 的前缀和 ,这样式子就变成了:
发现后面这个东西应该只和 有关,我们令这个差值为 ,则有:
令:
那么上式有:
现在需要快速求出 。先求 , 可以暴力做,此后递推:
求 ,同样 可以暴力做,此后递推:
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?