首先考慮如果固定了 \(a\),如何判定這個 \(a\) 是否能被排序。
如果存在 \(a_i > a_j(i < j)\),那麼 \(a_i\) 肯定要交換到 \(a_j\) 後面,那麼就肯定會交換 \(a_i, a_j\)。
於是合法條件就是如果存在 \(a_i > a_j(i < j)\),那麼 \(a_i, a_j\) 只相差一個二進位制位。
那就還能知道此時一定 \(a_i\) 這一位為 \(1\),\(a_j\) 這一位為 \(0\),其餘位都相同。
於是可以考慮從高位開始往低位比較兩個數字。
如果滿足 \(a_i, a_j(i < j)\) 前面已經遍歷的位都相同,這一位不同且 \(a_i\) 這一位為 \(1\),\(a_j\) 為 \(0\),就要求剩下的位 \(a_i, a_j\) 相同。
於是可以考慮 DP。
考慮到如果有了上面提到的情況,那麼此時 \(a_i, a_j\) 在低位就應該當作一個數了,相當於合併操作,同時還有提到從高位往低位去比較。 就可以設狀態 \(f_{n, m}\) 為最高位為 \(n\),當前有 \(m\) 個數的方案數。
接下來考慮轉移,分為兩種情況考慮:
-
不存在合併操作。
那麼這個時候對於 \(n\) 這一位,就應該滿足前面一部分為 \(0\),後面這部分為 \(1\),一共會有 \(m + 1\) 種可能的 \(01\) 分界線。同時考慮到此時先手可以操作使得這一位全變為 \(0\),那麼比較還得繼續到下一位。
有轉移 \(f_{n, m} = (m + 1)f_{n - 1, m}\)。
-
存在合併操作,即存在 \(i < j\),\(a_i\) 第 \(n\) 位為 \(1\),\(a_j\) 第 \(n\) 位為 \(0\)。
考慮找到最靠前的為 \(1\) 的位置 \(l\),最靠後的為 \(0\) 的位置 \(r\),此時肯定滿足 \(l < r\)。
那麼可以知道的是,不論 \(l < i < r\) 裡的 \(i\) 第 \(i\) 位是什麼,肯定都會和 \(l\) 合併或者和 \(r\) 合併。
於是可以知道,\(l\le i\le r\) 的 \(a_i\) 都會合並,還剩下 \(m - (r - l)\) 個數。同時如果先手也選擇了把這一位設成 \(0\),還需要輪到下一輪比較。
此時的方案數為中間 \(r - l - 1\) 個數這一位任選的 \(2^{r - l - 1}\)。
進一步的,考慮到對於同樣的 \(r - l\),不管 \(l, r\) 是多少,本質都是相同的,此時對應的有 \(m - (r - l)\) 種方案。
於是考慮列舉 \(m - (r - l)\),有轉移 \(f_{n, m} = \sum\limits_{i = 1}^{m - 1} i2^{m - i - 1} f_{n - 1, i}\)。
綜上,有轉移 \(f_{n, m} = (m + 1)f_{n - 1, m} + \sum\limits_{i = 1}^{m - 1} i2^{m - i - 1} f_{n - 1, i}\)。
對於轉移的最佳化,考慮拎出後面求和的部分,令 \(g_{n, m} = \sum\limits_{i = 1}^{m - 1} i2^{m - i - 1} f_{n - 1, i}\)。
那麼有 \(g_{n, m + 1} = \sum\limits_{i = 1}^m i 2^{m - i} f_{n - 1, i}\)。
可以發現有 \(g_{n, m + 1} = 2g_{n, m} + mf_{n - 1, m}\),可以遞推求出。
時間複雜度 \(\mathcal{O}(nm)\)。
#include<bits/stdc++.h>
using ll = long long;
constexpr ll mod = 1e9 + 7;
const int maxn = 5e3 + 10;
ll f[maxn], g[maxn];
int main() {
int n, m; scanf("%d%d", &n, &m);
std::fill(f + 1, f + m + 1, 1);
while (n--) {
std::copy(f + 1, f + m + 1, g + 1);
for (int i = 1; i <= m; i++)
f[i] = (f[i - 1] * 2 + g[i - 1] * (i - 1)) % mod;
for (int i = 1; i <= m; i++)
(f[i] += g[i] * (i + 1)) %= mod;
}
printf("%lld\n", f[m]);
return 0;
}