题解:P9004 [RC-07] Abnormal Permutation Tuples

· · 题解

这题竟然是多项式复杂度的没想到吧。

打一个 n=4 的表。把所有的排列按字典序排序,逆序对是这样的:

0 1 1 2 2 3 1 2 2 3 3 4 2 3 3 4 4 5 3 4 4 5 5 6

可以注意到这玩意能写成这样:

0 1 1 2 2 3/1 2 2 3 3 4/2 3 3 4 4 5/3 4 4 5 5 6

分成这样四块,可以发现每块都是几乎一样的,只是分别加上了 0,1,2,3,很明显是排列第一位对逆序对数的贡献。并且第一个块是 n=3 的表。

直接考虑 DP,设 dp_{i,a,b,c,j} 表示 i 阶排列,选择的排列序列开头的逆序对数是 a,结尾是 b,总共有 c 个排列,且当前考虑到排列的第 j 个块。

那么可以把前面的和当前块拼起来,有转移(有点长我就放行间公式了):

dp_{i,a,e,c+f,j}\gets dp_{i,a,b,c,j-1}\times dp_{i-1,d-j+1,e-j+1,f,i-1}[b>d]

其中 d,e,f 是枚举的,表示当前块的最大值和最小值,以及个数。 :::info[对转移式的解释] 两个序列拼在一起,最大值显然是 a 最小值显然是 e 因为序列的逆序对数是降序的。排列个数显然是 c+f。

::: 同时,序列可以直接在这个块开头,前面没有东西。 $$ dp_{i,d,e,c+f,j}\gets dp_{i-1,d-j+1,e-j+1,f,i-1} $$ 解释和上面类似。 $j$ 显然可以滚动数组掉,DP 数组可以少一维。 $i,j$ 是 $O(n)$ 的,$a,b,d,e$ 是 $O(n^2)$,$c,f$ 是 $O(m)$,全部乘起来可以得到复杂度 $O(n^{10}m^2)$。显然是过不了的。 发现你还可以前缀和优化掉 $b$,复杂度变成了 $O(n^8m^2)$。然后你会发现这个东西常数极其小,随便剪枝一下就过了!具体可以看代码。 洛谷机子比较慢,可能卡常会比较难受。 :::success[code] ``` #include<bits/stdc++.h> using namespace std; #define int long long #define ui unsigned int #define i128 __int128_t #define u128 __uint128_t #define i16 short #define u16 unsigned i16 #define fi first #define se second #define pii pair<int,int> #define lowbit(x) ((x)&(-(x))) #ifdef int #define i32 signed #define u32 unsigned int #define inf 1e18 #define popc(x) __builtin_popcountll(x) #define ctz(x) __builtin_ctzll(x) #define clz(x) __builtin_clzll(x) #else #define ll long long #define ull unsigned ll #define inf 1e9 #define infl 1e18 #define popc(x) __builtin_popcount(x) #define ctz(x) __builtin_ctz(x) #define clz(x) __builtin_clz(x) #define popcl(x) __builtin_popcountll(x) #define ctzl(x) __builtin_ctzll(x) #define clzl(x) __builtin_clzll(x) #define pll pair<ll,ll> #endif #define push emplace #define pb emplace_back #define pf emplace_front #define ppb pop_back #define ppf pop_front #define fileio(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout) const int N=1e6+10,mod=1e9+7; int n,m,p; int ff[25][110][110][35];//i 阶排列,以几个逆序对数开头,几个逆序对数结尾的几链数量 int g[25][110][110][35]; int ss[25][110][110][35]; signed main() { ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); cin>>n>>m>>p; ff[1][0][0][1]=1; for(int i=2;i<=n;i++) { int s=i*(i-1)/2,t=(i-1)*(i-2)/2; for(int j=1;j<=i;j++) { memset(g[i],0,sizeof(g[i])); for(int d=j-1;d<s;d++) for(int f=1;f<m;f++) for(int e=j-1;e<=d;e++) if(ff[i-1][d-j+1][e-j+1][f]) for(int c=1;c+f<=m;c++) for(int a=d+1;a<=s;a++) if(ss[i][a][a][c]!=ss[i][a][d][c]) (g[i][a][e][f+c]+=(ss[i][a][a][c]-ss[i][a][d][c]+p)*ff[i-1][d-j+1][e-j+1][f])%=p; for(int d=j-1;d<=t+j-1;d++) for(int e=j-1;e<=d;e++) for(int f=1;f<=m;f++) g[i][d][e][f]+=ff[i-1][d-j+1][e-j+1][f]; for(int d=0;d<=s;d++) for(int e=0;e<=d;e++) for(int f=1;f<=m;f++) (ff[i][d][e][f]+=g[i][d][e][f])%=p; for(int a=0;a<=s;a++) for(int c=1;c<=m;c++) ss[i][a][0][c]=ff[i][a][0][c]; for(int a=0;a<=s;a++) for(int b=1;b<=a;b++) for(int c=1;c<=m;c++) ss[i][a][b][c]=(ss[i][a][b-1][c]+ff[i][a][b][c])%p; } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int ans=0; for(int a=0;a<=i*(i-1)/2;a++) for(int b=0;b<=a;b++) (ans+=ff[i][a][b][j])%=p; cout<<ans<<" \n"[j==m]; } return 0; } ``` :::