ARC068FSolitaire 前缀和优化dp

Posted ck6100lgev2

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ARC068FSolitaire 前缀和优化dp相关的知识,希望对你有一定的参考价值。

Description

? 你有一个双端队列和 N 个数字,先按 1到 N 的顺序每次从任意一端插入当前数字,再进行 N 次操作每次可以从两端弹出,求有多少种弹出序列满足第 K 位为 1。

Input

? 一行两个整数 N 和 K。

Output

? 一个整数表示答案,对 10^9+7取模。

Sample Input

Sample #1
2 1

Sample #2
17 2

Sample #3
2000 1000

Sample Output

Sample #1
1

Sample #2
262144

Sample #3
674286644

HINT

1≤K≤N≤2000

Sol

显然我们发现这个生成的序列一定是一个V字形,1是最底端。

然后这个删除序列一定有这样一个性质:构造删除序列的时候,新加入的数字要么是未出现过的数字的最大值,要么严格小于出现过的数字的最小值。

证明:我们假设当前最小值是从左边取出来的,那么从左边取一定是严格小于它的,从右边取的话,你不可能取出小于未出现过的数字的最大值的数,因为这个未出现的最大值还没有被取出来,比他小的自然也取不出来。而如果从最小值到最大值都取了的话,你下一步取出的一定是新的最小值。

所以我们设(f[i][j])表示删除序列大小为i,当前最小值为j的方案数,那么(f[i][j]=sum_{k=j}^{n}f[i-1][k]),也就是要么原来的最小值就是j,这次取出来了一个未出现的最大值,要么这次取的数是j。

用前缀和优化可以做到(O(n^2))

但是有个问题,1可能在k位之前就出现了,所以(f[k][1])不是答案,答案是(f[k][1]-f[k-1][1]),这样就减去了1提前出现的情况,之后后面的序列方案数就是(2^{n-k-1}),即:枚举它是从左边还是右边出来的。

Code

#include <cstdio>
int n,f[2005][2005],s[2005],k,P=1e9+7,a;
int main()
{
    scanf("%d%d",&n,&k);f[0][n+1]=1;
    for(int i=1;i<=k;i++) for(int j=n+1;j;j--) s[j]=(s[j+1]+f[i-1][j])%P,f[i][j]=(j<=n-i+1?s[j]:0);
    a=(f[k][1]-f[k-1][1]+P)%P;
    for(int i=1;i<=n-k-1;i++) a=(a+a)%P;
    printf("%d
",a);
}

以上是关于ARC068FSolitaire 前缀和优化dp的主要内容,如果未能解决你的问题,请参考以下文章

做题arc068_f-Solitaire——糊结论

arc068ESnuke Line

[Arc068D/At2299] Card Eater - 结论

ARC068E Snuke Line

arc068 E: Snuke Line

做题arc068_e-Snuke Line——利用特殊性质分讨