题意:
给你1~n的排列,全部插入双端队列,最终队列必须满足1的左边数列递减,右边递增,即一个V型,再以任意顺序全部弹出。问第k个弹出的数为1的方案有多少种(mod 1e9+7),注意,两个方案当且仅当它们某一次弹出的数不同时视为不同。
题解:
首先我们要推出个有用的结论来转化题意,在这里,我们的重点是每次弹出的数。我们注意到每次能弹出的数为队首或队尾,要么是左边最大,要么是右边最大,显然其中一个必然是剩下全部数中最大的,不妨设是队首。又因为以前左边弹出的数都比队首大,右边弹出的数都比队尾大,所以队尾必然比所有以前弹出的所有数都小,所以队尾必然比最小值小。整理一下,每次能弹出的数只有两种可能
1. 没弹的数中最大
2. 比已弹出的数中最小还要小
用 表示已弹i个数,这i个数的最小值为j的方案数。
转移时有两种情况:一种是这次(第 次)弹出j,那么只要上一次时最小值大于j即可,同时这时j满足条件2,所以可以弹出j;因为是排列,只有一个j,所以这种情况下方案数为
另一种是以前最小为j,这次弹出数比j大,要转移过来的状态显然为dp[i-1][j],但这时要满足 剩下的数中才有比j大的数,否则这种情况下方案数为0。在剩下的数中有比j大的数的情况下,条件2显然不能满足(要弹出的数比j大),对于dp[i-1][j]中每一种确定的方案,没弹出的数字也确定了,因为是排列,没弹的数中最大的数也只有对应的一个,所以这种情况下方案数为
那么最终方程为 ,用s[i]数组做前缀和优化,时间复杂度为O( )
代码:
#include <iostream>
#include <stdio.h>
using namespace std;
#define N 2005
const int mod=1e9+7;
int n,k;
int dp[N][N],ss[N];
int MUL(int a,int b)
{
int ret=0;
while(b)
{
if((b&1))
ret=(ret+a)%mod;
b>>=1;
a=(a+a)%mod;
}
return ret;
}
int KSM(int a,int p)
{
int ret=1;
while(p)
{
if((p&1))
ret=MUL(ret,a);
p>>=1;
a=MUL(a,a);
}
return ret;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
dp[1][i]=1,ss[i]=n-i+1;
for(int i=2;i<=k;i++)
for(int j=n;j>=1;j--)
{
dp[i][j]=(i+j-1<=n?ss[j]:0);
ss[j]=(ss[j+1]+dp[i][j])%mod;
}
printf("%d\n",MUL(KSM(2,n-k-1),((dp[k][1]-dp[k-1][1])%mod+mod)%mod));
return 0;
}