Pre
调了好久好久。(QAQ)
高高兴兴的打了一个(DP)数组的转移的程序,直接(T)掉,后来看了题解发现有优化。
Solution
状态转移有三个部分组成,一个是左右子树,一个是矩形。
依次设置为(i,j,k),可以枚举,时间复杂度太高。
新开一个数组,存下当前状态下的(i+j)的总答案,时间复杂度(O(n^2)),在枚举(k),状态转移,时间复杂度(O(n^2))
Code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 1000000007, N = 550, M = 1000000 + 5;
int a[N], n, K;
int stk[N], top;
int ch[N][2], fa[N], sz[N];
ll dp[N][N], fac[M], f[N][N];
inline ll Pow (ll u, ll v) {
ll tot = 1, base = u;
while (v) {
if (v % 2 == 1) {
tot = tot * base % mod;
}
base = base * base % mod;
v /= 2;
}
return tot;
}
inline ll C (ll u, ll v) {
return fac[u] * Pow (fac[v], mod - 2) % mod * Pow (fac[u - v], mod - 2) % mod;
}
inline void dfs (int p, int v) {
sz[p]++;
if (ch[p][0]) {dfs (ch[p][0], a[p]);sz[p] += sz[ch[p][0]];}
if (ch[p][1]) {dfs (ch[p][1], a[p]);sz[p] += sz[ch[p][1]];}
for (int i = 0; i <= K; ++i) {
for (int j = 0; j <= K; ++j) {
if (i + j > K) {break;}
f[p][i + j] = (f[p][i + j] + dp[ch[p][0]][i] * dp[ch[p][1]][j] % mod) % mod;
}
}
for (int i = 0; i <= K; ++i) {
if (i > a[p] - v) {break;}
for (int j = 0; j <= K; ++j) {
if (i + j > K || i + j > sz[p]) {break;}
dp[p][i + j] = (dp[p][i + j] + f[p][j] * fac[a[p] - v] % mod * Pow (fac[a[p] - v - i], mod - 2) % mod * C (sz[p] - j, i) % mod) % mod;
}
}
}
int main () {
scanf ("%d%d", &n, &K);
fac[0] = 1; f[0][0] = 1; dp[0][0] = 1;
for (int i = 1; i <= M - 5; ++i) {
fac[i] = fac[i - 1] * i % mod;
}
for (int i = 1; i <= n; ++i) {
scanf ("%d", &a[i]);
while (top > 0 && a[stk[top]] > a[i]) {ch[i][0] = stk[top], top--;}
if (ch[fa[ch[i][0]]][0] == ch[i][0]) {ch[fa[ch[i][0]]][0] = 0;}
else {ch[fa[ch[i][0]]][1] = 0;}
fa[ch[i][0]] = i;
fa[i] = stk[top];
if (fa[i]) {ch[fa[i]][1] = i;}
stk[++top] = i;
}
int rt = 0;
for (int i = 1; i <= n; ++i) {if (!fa[i]) {rt = i;break;}}
dfs (rt, 0);
printf ("%lld
", dp[rt][K] % mod);
return 0;
}
Conclusion
这个优化方法有意思。