P6453 [COCI2008-2009#4] PERIODNI 题解
Laoshan_PLUS · · 题解
一道广为人知的笛树题。既然 SPOJ 交不了了双倍经验没了,那就在这里交一发题解。
事先感谢 @Dyc780511 大佬。
这道题运用了一个技巧:将图建成树。
考虑到如果有一个最小的棋盘将两边分隔开,那么除去这个棋所在的行以外,剩下的两组棋盘互不干扰。即对于
先考虑棋子编号在区间
注意此时为了不让数组刚更新的内容去更新其他内容,因此
注意此时为了不让数组刚更新的内容去更新其他内容,因此
#include <bits/stdc++.h>
#define inv(i) (power(fac[i], MOD - 2))
#define int long long
using namespace std;
constexpr int MAXN = 505, MOD = 1e9 + 7, MAXH = 1e6 + 5;
int n, k, a[MAXN];
int ls[MAXN], rs[MAXN], rt, siz[MAXN];
stack<int> st;
int fac[MAXH];
int f[MAXN][MAXN];
int power(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
void init() {
fac[0] = 1;
for (int i = 1; i < MAXH; i++) fac[i] = fac[i - 1] * i % MOD;
}
int A(int n, int m) {
if (n < m || n < 0 || m < 0) return 0;
return fac[n] * inv(n - m) % MOD;
}
int C(int n, int m) {
if (n < m || n < 0 || m < 0) return 0;
return fac[n] * inv(m) % MOD * inv(n - m) % MOD;
}
void insert(int k, int w) {
while (!st.empty() && w < a[st.top()]) {
ls[k] = st.top();
st.pop();
}
if (!st.empty()) rs[st.top()] = k;
else rt = k;
st.push(k);
}
void dfs(int x, int fno) {
siz[x] = 1;
if (ls[x]) dfs(ls[x], x);
if (rs[x]) dfs(rs[x], x);
siz[x] += siz[ls[x]] + siz[rs[x]];
f[x][0] = 1;
for (int i = k; i >= 1; i--)
for (int j = 1; j <= min(i, siz[ls[x]]); j++)
f[x][i] = (f[x][i] + f[ls[x]][j] * f[x][i - j] % MOD) % MOD;
for (int i = k; i >= 1; i--)
for (int j = 1; j <= min(i, siz[rs[x]]); j++)
f[x][i] = (f[x][i] + f[rs[x]][j] * f[x][i - j] % MOD) % MOD;
for (int i = min(k, siz[x]); i >= 1; i--)
for (int j = 1; j <= i; j++)
f[x][i] = (f[x][i] + f[x][i - j] * C(a[x] - a[fno], j) % MOD * A(siz[x] - i + j, j) % MOD) % MOD;
}
signed main() {
init();
scanf("%lld%lld", &n, &k);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]), insert(i, a[i]);
dfs(rt, 0);
cout << f[rt][k] << '\n';
return 0;
}