Description
有一个双端队列,按照1…n的顺序把n个卡片放进队头或队尾得到一个满的队列
然后每次可以从头或从尾取一个数字取n次,这样得到一个长度为n的序列。问1恰好在k这个位置的方案数
Solution
这题好劲啊
可以发现几个小性质。首先队列里的元素一定是先减后增的,并且由于1在k这里,所以前k-1个数字必须是一个或两个单调下降序列,而后面n-k-1个随便从队列两端选。然后我就不会做了。。
具体做法可以看这个blog
讲一下我的理解吧,可能不太对。
考虑设f[i,j]表示选了i个数字,最小的是j的方案数。假设我们已经得到了2个单调的序列,那么第i个一定只能放一个小于最小值的数字,不然我们只能把它插在某个序列的中间,而这种方案一定已经被算过一次了
那么n3的dp套一个前缀和优化就可以做到n2了
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
const int MOD=1e9+7;
const int N=2005;
int f[N][N],s[N][N];
int ksm(int x,int dep) {
int res=1;
for (dep=std:: max(dep,0);dep;dep>>=1,x=1LL*x*x%MOD) {
(dep&1)?(res=1LL*res*x%MOD):0;
}
return res;
}
int main(void) {
int n,k; scanf("%d%d",&n,&k);
if (n==k) {
}
f[0][n+1]=1;
drp(j,n+1,1) {
s[0][j]=s[0][j+1]+f[0][j];
(s[0][j]>=MOD)?(s[0][j]-=MOD):0;
}
rep(i,1,k) {
rep(j,1,n-i+1) f[i][j]=s[i-1][j];
drp(j,n+1,1) {
s[i][j]=s[i][j+1]+f[i][j];
(s[i][j]>=MOD)?(s[i][j]-=MOD):0;
}
}
int ans=f[k][1]-f[k-1][1];
(ans<0)?(ans+=MOD):0;
ans=1LL*ans*ksm(2,n-k-1)%MOD;
printf("%d\n", ans);
return 0;
}