[arc068f]Solitaire

来源:互联网 发布:seo外包顾问服务 编辑:程序博客网 时间:2024/05/17 04:47

题目大意

将1-n顺序加入双端队列(每次可加头可加尾),再删除(每次可删头可删尾),求有多少种删除序列,使得1是第k个被删的。

做法

考虑什么样的删除序列存在对应的加入序列且合法。
1、第k个是1。
2、前k个元素能拆分成两个单调下降序列。
3、第k个后的元素每个位置都大于等于后缀最大值或小于等于后缀最小值。
4、前k个元素拆分出的单调下降序列其中一个的最小值大于等于第k个后的元素的最大值。
这些性质不难发现。
然后我们考虑一种贪心划分单调下降序列的方法,使得任意划分方法能扭转成这种划分,那就是能往第一个塞就往第一个塞,否则往第二个塞。
设f[i,j,t=1/2]表示把n~i这些元素分配给两个单调下降序列,其中有j个第二个单调下降序列的末尾元素没有加入到删除序列中,第i个元素属于第t个单调下降序列的方案数。
i-1如果放到第二个序列,则不能马上放入到删除序列中否则违背贪心划分,因此转移到f[i-1,j+1,2]。
i-1如果放到第一个序列,则原本不能放入到删除序列的第二个单调下降序列元素均可放入到删除序列,求后缀和即可。
最终方案是f[1,n-k,1],后面的显然可以2的次幂。

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;const int maxn=2000+10,mo=1000000007;int f[maxn][maxn][3],two[maxn];int i,j,k,l,t,n,m,ans;int main(){    scanf("%d%d",&n,&k);    two[0]=1;    fo(i,1,n) two[i]=(ll)two[i-1]*2%mo;    f[n+1][0][2]=1;    fd(i,n+1,2){        fd(j,n,0) (f[i][j][1]+=f[i][j+1][1])%=mo;        fo(j,0,n)            fo(t,1,2){                (f[i-1][j+1][2]+=f[i][j][t])%=mo;                (f[i-1][j][1]+=f[i][j][t])%=mo;            }    }    ans=f[1][n-k][1];    ans=(ll)ans*two[max(n-k-1,0)]%mo;    (ans+=mo)%=mo;    printf("%d\n",ans);}