将
题解
根据套路这种计数类题我们应该考虑什么样的序列满足性质。
我们考虑我们是如何构造这段序列的。
一开始,我们将

然后我们考虑取出后哪些数是可以放在

大概就应该是上图红色和蓝色的(当然分成两段的可能是红色而不是绿色)。
于是我们发现

我们发现如果是这样的话,与
于是我们就可以愉快地 dp 辣!
显然后面那段绿色的我们是可以随便选的,即每次选左边和选右边都可以,也就是
我们考虑如何 dp 出前面的序列。
一个非常 simple 的想法是令
我们有如下贪心:我们尽量把小的数扔给红色序列(因为蓝色序列的最小值越大越可能符合条件)。如果
但由于我们无法枚举 dp 行不通,但我们得到了一个有趣的贪心策略。
有上面的贪心策略,我们发现只要我们能知道所有合法的
我们考虑对于一个状态
- 选择一个最大的数放入蓝色序列,当然前提条件是存在这个大于
的数。由于这个数不可能放在红色序列里了,且如果不选最大的那个而去选其他大于 的数,那也就意味着这个最大的数要给绿色序列。而这样一来绿色序列就会有一个数比蓝色序列大,这显然是不合法的。也就是 。 - 选择一个比
小的数给红色序列。也就是
这样做复杂度
我们发现我们并不必关心
我们令
- 选择一个最大的数放入蓝色序列。
。 - 选择一个比
小的数给红色序列。 。
发现第一维可以直接压掉,然后直接 dp 即可。
代码
#include <iostream>
using namespace std;
typedef long long LL;
const int maxn = 2005;
const int mod = 1000000007;
inline int pow(int a, int b)
{
if (b < 0)
return 1;
int ans = 1;
for (; b; b >>= 1, a = (LL) a * a % mod)
if (b & 1)
ans = (LL) ans * a % mod;
return ans;
}
int dp[maxn];
int main()
{
int n, k;
cin >> n >> k;
dp[n + 1] = 1;
for (int i = 1; i <= k; ++i)
{
for (int j = n; j; --j)
{
if (n - j + 1 < i)
dp[j] = 0;
else
{
dp[j] = dp[j + 1] + dp[j];
if (dp[j] >= mod)
dp[j] -= mod;
}
}
}
cout << (LL) dp[1] * pow(2, n - k - 1) % mod;
return 0;
}