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的主要内容,如果未能解决你的问题,请参考以下文章