码迷,mamicode.com
首页 > 其他好文 > 详细

【ARC068F】Solitaire 前缀和优化dp

时间:2018-08-08 21:22:56      阅读:125      评论:0      收藏:0      [点我收藏+]

标签:out   put   [1]   line   include   下一步   插入   des   顺序   

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\n",a);
}

【ARC068F】Solitaire 前缀和优化dp

标签:out   put   [1]   line   include   下一步   插入   des   顺序   

原文地址:https://www.cnblogs.com/CK6100LGEV2/p/9445414.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!