1. 程式人生 > >【arc068F】Solitaire

【arc068F】Solitaire

iostream arc algorithm -i str 下一個 pac 滿足 ()

題目大意

有一個隊列,頭尾都可以進出
首先將n個數1~n從小到大扔進隊列,然後將一次彈出隊列,求最後彈出來的排列中,第k個數為1的排列有多少種。

解題思路

我們來考慮一下一個合法排列的性質,

第k個數是1
前k-1個數是可以拆成一個或兩個單調遞減的序列。
前k-1個數中其中一個序列的最小值一定大於後n-k個數中的最大值。

考慮如何來滿足這個構造出這個排列。
先考慮後n-k-1個數,發現,這些數一定是有一個單調的隊列,每次彈出頭或尾來構成的,只要我們確定前k-1個數,就可以得出這個單調的隊列能構成的後n-k-1個數的方案,就是\(2^{n-k-1}\)
然後,如何確定前k-1個數,且保證第2條性質呢?

設f[i][j]表示,前k-1個數中,已確定了i個數,在確定的i個數中,最小值為j。
假定第一個單調序列是彈出1個一遍,那麽第二個單調序列就要滿足第3個性質。
每次新加一個數,如果加進第一個單調序列,顯然加入的數就要小於j
於是f[i][j]->f[i+1][k] (j>k)
如果如果加進第二個單調序列,顯然加入的數就要是當前沒加的數中最大的,只有這樣才能滿足第3條性質。
於是f[i][j]->f[i+1][j]。
註意一下邊界。

#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <bitset>
#include <set>
const int maxlongint=2147483647;
const long long mo=1e9+7;   
const int N=3005;
using namespace std;
int n,k;
long long ans,mi[N],f[N][N];
int main()
{
    scanf("%d%d",&n,&k);
    mi[0]=1;
    for(int i=1;i<=n;i++) mi[i]=mi[i-1]*2%mo;
    for(int i=n;i>=2;i--) f[1][i]=1;
    for(int i=1;i<k-1;i++)
    {
        long long sum=f[i][n-i+1];
        for(int j=n-i;j>=2;j--)
        {
            sum=(sum+f[i][j])%mo;
            f[i+1][j]=(f[i+1][j]+sum)%mo;
        }
    }
    for(int j=2;j<=n-k+2;j++) ans=(ans+f[k-1][j])%mo;
    if(k==1) ans=1;
    if(n-1-k<0) printf("%lld",ans);
    else
        printf("%lld",ans*mi[n-1-k]%mo);
}

【arc068F】Solitaire