题目
你需要构造两个长度为n的排列,分别记为p,q,满足:
①p的字典序比q的字典序小
②p的逆序对比q的逆序对多
答案对mod(1<=mod<=1e9)取模
easy version:n<=50
hard version:n<=500
思路来源
官方题解
题解1
f[i][j]:表示长度为i,逆序对为j对的方案数,枚举第i位填的数,
考虑1-i时的从小到大的rank几,比如rank为j,则[j+1,i]与其产生了j-i个逆序对
sum[i][j]:表示f的前缀和
g[i]:表示长度为i的排列,第一个位置处p<q,但是整个排列总逆序对数p>q的方案数
算g的时候,枚举第一个位置p填数k,q填数l(k<l),再枚举p在这i位共有w个逆序对,
则p序列后i-1位有w-k个逆序对,q序列后i-1位需要少于w-l个逆序对
ans[i]:表示长度为i,满足p字典序小于q,且总逆序对数>q的方案数
考虑长度为i的ans怎么计算,
要么第一位两个数是相等的,有i种选法,从ans[i-1]转移
要么是不等的,从g[i]转移
题解2
待补
代码1(n<=50):
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=51,M=1255;
int n,mod,f[N][M],sum[N][M],g[N],ans[N];
//f[i][j]:长度为i 逆序对为j对的方案数 枚举最后一个值产生的逆序对数
//sum[i][j]:前缀和
//g[i]:长度为i 第一个位置p<q 保证总逆序对数p>q的方案数
//ans[i]:长度为i p<q 保证总逆序对数p>q的方案数
void add(int &x,int y){
x+=y;
x%=mod;
}
int main(){
scanf("%d%d",&n,&mod);
add(f[1][0],1);
for(int j=1;j<M;++j){
add(sum[1][j],1);
}
for(int i=2;i<=n;++i){
for(int j=0;j<M;++j){
add(f[i][j],sum[i-1][j]);
if(j-i>=0)add(f[i][j],mod-sum[i-1][j-i]);
sum[i][j]=f[i][j];
if(j-1>=0)add(sum[i][j],sum[i][j-1]);
}
}
for(int i=1;i<=n;++i){//长度为i 枚举第一位p<q
for(int k=1;k<=i;++k){//p
for(int l=k+1;l<=i;++l){//q
for(int w=0;w<M;++w){//后面i-1位 p有w-(k-1)对 q少于w-(l-1)对
if(w-(l-1)-1<0)continue;
add(g[i],1ll*f[i-1][w-(k-1)]*sum[i-1][w-(l-1)-1]%mod);
}
}
}
}
for(int i=2;i<=n;++i){
add(ans[i],(g[i]+1ll*i*ans[i-1]%mod)%mod);
}
printf("%d\n",ans[n]);
return 0;
}
代码2(n<=500):
待补