CF1542E1 Abnormal Permutation Pairs (easy version) 题解
CF1542E1 Abnormal Permutation Pairs (easy version) 题解
不会 Hard version
对于第一个限制字典序,我们可以考虑枚举前
我们设
因为逆序对仅与相对大小有关,所以后
对于第一种转移,我们只需把上一层所有数作为开头的方案加起来;对于第二种转移,我们考虑
其中第二个转移是
然后我们来考虑统计答案。我们设逆序对个数上界
我们从
显然是一个
会发现后半部分是个矩形内的和,可以用二维前缀和优化到
简单版就做完了,复杂度
代码:
#include<bits/stdc++.h> using namespace std; const int N = 54; int mod; void add(int &a, int b) { a = (a+b)%mod; } int f[N][N][N*N]; int n; int fac[N]; int sum1[N][N][N*N];//第一个前缀和(可以滚动数组优化) int sum2[N][N][N*N];//第二个前缀和(也可以滚动数组) int C[N][N]; void prework() { fac[0] = 1; for(int i = 1; i<=n; ++i) { fac[i] = 1ll*fac[i-1]*i%mod; } C[0][0] = 1; for(int i = 1; i<=n; ++i) { C[i][0] = 1; for(int j = 1; j<=i; ++j) { C[i][j] = (C[i-1][j] + C[i-1][j-1])%mod; } }//预处理阶乘和组合数 } int main() { scanf("%d%d", &n, &mod); prework(); f[1][1][0] = 1; for(int i = 2; i<=n; ++i) { for(int j = 1; j<i; ++j) { for(int k = i-1; k<=(i*(i-1)/2); ++k) { add(f[i][i][k], f[i-1][j][k-(i-1)]); } }//新加入一个最大的数,让它作为开头 for(int j = 1; j<i; ++j) { sum1[i-1][j][0] = f[i-1][j][0]; for(int o = 1; o<=(i*(i-1)/2); ++o) { sum1[i-1][j][o] = f[i-1][j][o]; add(sum1[i-1][j][o], sum1[i-1][j][o-1]); }//计算需要的前缀和 for(int o = 0; o<=(i*(i-1)/2); ++o) { if(o > i-2) { add(f[i][j][o], (sum1[i-1][j][o] - sum1[i-1][j][o-(i-2)-1])%mod); if(f[i][j][o] < 0) f[i][j][o]+=mod; } else f[i][j][o] = sum1[i-1][j][o]; } }//对于其他数作为开头的情况 } int ans = 0; for(int i = 0; i<n-1; ++i) { int ret = 1ll*fac[i]*C[n][i]%mod; int len = n-i; int sum = 0; for(int j = 1; j<=len; ++j) { sum2[len][j][0] = f[len][j][0]; add(sum2[len][j][0], sum2[len][j-1][0]); for(int k = 1; k<=(len*(len-1)/2); ++k){ sum2[len][j][k] = f[len][j][k]; add(sum2[len][j][k], (1ll * sum2[len][j][k-1] + 1ll * sum2[len][j-1][k] + 1ll * mod - 1ll*sum2[len][j-1][k-1])%mod); } }//预处理二维前缀和。 for(int j = 1; j<=len; ++j) { for(int oa = 1; oa<=(len*(len-1)/2) ;++oa) { add(sum, 1ll*f[len][j][oa] * (1ll*sum2[len][len][oa-1] - sum2[len][j][oa-1])%mod); } } add(ans, 1ll*ret*sum%mod); } if(ans<0) ans+=mod; printf("%d\n", ans); return 0; }