【题解】[Codechef] Beautiful Permutation
传送门
以此纪念我场切的 dp。
这种计数的类型一看就很 dp 的样子。考场上一开始设的 dp 状态是 \(dp_{i,j,k_1,k_2,0/1}\) 表示将前 \(i\)个数分为 \(j\) 段,放了 \(k_1\) 个偶数,\(k_2\) 个奇数,当前段为偶数段或奇数段的方案数。考虑如何转移,记 \(cnt_0\) 表示序列中可填入的偶数个数,\(cnt_1\) 表示序列中可填入的奇数个数。
当 \(i\) 为空时,可以写出四个转移:
\[dp_{i,j,k_1,k_2,0} = (cnt_0-k_1+1)(dp_{i-1,j,k_1-1,k_2,0} + dp_{i-1,j-1,k_1-1,k_2,1})
\]
\[dp_{i,j,k_1,k_2,1}=(cnt_1-k_2+1)(dp_{i-1,j,k_1,k_2-1,1} + dp_{i-1,j-1,k_1,k_2-1,0})
\]
当 \(i\) 为非空时,可以根据 \(a_i\) 的奇偶性写出两个转移:
\[dp_{i,j,k_1,k_2,0} = dp_{i-1,j,k_1,k_2,1} + dp_{i-1,j-1,k_1,k_2,0} (a_i 为奇数)
\]
\[dp_{i,j,k_1,k_2,1} = dp_{i-1,j-1,k_1,k_2,1} + dp_{i-1,j,k_1,k_2,0} (a_i 为偶数)
\]
可以把第一维滚掉,这样就做到了时间复杂度 \(O(n^4)\),空间复杂度 \(O(4n^3)\)。70分到手。
我们发现记前 \(i\) 个数中空位为 \(sum_i\) 个,则填了 \(k_2\) 个奇数时必然填了 \(sum_i-k_2\) 个偶数。偶数的数量可以被奇数固定下来,于是重新设状态 \(dp_{i,j,k_2,0/1}\) 表示将前 \(i\)个数分为 \(j\) 段,\(k_2\) 个奇数,当前段为偶数段或奇数段的方案数。则转移变为:
当 \(i\) 为空时:
\[dp_{i,j,k_2,0} = (cnt_0-(sum_i-k_2)+1)(dp_{i-1,j,k_2,0} + dp_{i-1,j-1,k_2,1})
\]
\[dp_{i,j,k_2,1}=(cnt_1-k_2+1)(dp_{i-1,j,k_2-1,1} + dp_{i-1,j-1,k_2-1,0})
\]
当 \(i\) 为非空时:
\[dp_{i,j,k_2,0} = dp_{i-1,j,k_2,1} + dp_{i-1,j-1,k_2,0} (a_i 为奇数)
\]
\[dp_{i,j,k_2,1} = dp_{i-1,j-1,k_2,1} + dp_{i-1,j,k_2,0} (a_i 为偶数)
\]
时间复杂度为 \(O(n^3)\),空间复杂度 \(O(2n^3)\)。可通过此题。
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define mod 1000000007
using namespace std;
const int N = 305;
int n, m, a[N], cnt[N], dp[N][N][N][2], sum;
signed main() {
freopen("2475.in", "r", stdin);
freopen("2475.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m;
For(i,1,n) {
cin >> a[i];
if(!a[i]) continue;
cnt[a[i] & 1]++;
}
cnt[0] = n / 2 - cnt[0];
cnt[1] = ceil(1.0 * n / 2) - cnt[1];
dp[0][0][0][0] = dp[0][0][0][1] = 1;
For(i,1,n) {
if(!a[i]) sum++;
For(j,1,m) {
For(k2,0,min(sum, cnt[1])) {
if(a[i] != 0) {
if(a[i] & 1) {
dp[i][j][k2][1] = (dp[i][j][k2][1] + (dp[i-1][j-1][k2][0] + dp[i-1][j][k2][1]) % mod) % mod;
} else {
dp[i][j][k2][0] = (dp[i][j][k2][0] + (dp[i-1][j][k2][0] + dp[i-1][j-1][k2][1]) % mod) % mod;
}
continue;
}
if(cnt[0] - (sum - k2) + 1 > 0) dp[i][j][k2][0] = (dp[i][j][k2][0] + ((cnt[0] - (sum - k2) + 1) * (dp[i-1][j-1][k2][1] + dp[i-1][j][k2][0]) % mod) % mod) % mod;
if(k2 != 0) dp[i][j][k2][1] = (dp[i][j][k2][1] + ((cnt[1] - k2 + 1) * (dp[i-1][j-1][k2-1][0] + dp[i-1][j][k2-1][1]) % mod) % mod) % mod;
}
}
}
cout << (dp[n][m][cnt[1]][0] + dp[n][m][cnt[1]][1]) % mod << '\n';
return 0;
}