AtCoder Regular Contest 068F Solitaire dp

123 篇文章 0 订阅
36 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值