0
点赞
收藏
分享

微信扫一扫

【模拟赛】假如有一个按钮。。。(动态规划,卡特兰数)

小a草 2022-03-24 阅读 29

背景

在这里插入图片描述
在这里插入图片描述

题面

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

我们先不考虑第 k k k 个位置后面的,先想个办法判断 b b b 序列前面是否合法。

1 1 1 还没出现的时候,考虑逆向操作,每次抽出 b b b 开头的数可以选择添加到 a a a 序列两端,从两边往中间填。还没出现的 1 1 1 相当于将 a a a 砍成了两个单调下降的序列,我们每次可以选择在其中一个序列的末尾添加。

我们用这样一种贪心思路加,每次如果两个序列末尾都能容纳当前数,就加入末尾更小的那一个。这样不难发现从始至终两个序列末尾的大小关系都没变。

我们改变一种计数策略,用每个数前面比它小的数的个数 d [ i ] d[i] d[i] 来刻画排列。我们把 d p [ i ] [ j ] dp[i][j] dp[i][j] 的状态 j j j 定为当前(抽出了 i i i 个数)末尾小的序列的最短前缀长度,满足删除该前缀后此序列不再是末尾更小的序列。那么 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j] 转移到 d p [ i ] [ . . . ] dp[i][...] dp[i][...] 时, d [ i ] d[i] d[i] 就必须要在 [ 0 , j ] [0,j] [0,j] 之间,如果 d [ i ] d[i] d[i] 选择 0,那么该数将会放入末尾小的那个序列,转移到 j + 1 j+1 j+1 ,否则放入另一个序列,转移到 d [ i ] d[i] d[i]

我们发现,除了 d p [ 0 ] [ 0 ] = 1 dp[0][0]=1 dp[0][0]=1 以外, d p [ i ] [ 0 ] dp[i][0] dp[i][0] 都是 0,且有状态转移
d p [ i ] [ j ] = ∑ l = j − 1 i − 1 d p [ i − 1 ] [ l ] dp[i][j]=\sum_{l=j-1}^{i-1}dp[i-1][l] dp[i][j]=l=j1i1dp[i1][l]

我们令 s u f [ i ] [ j ] = ∑ l = j i d p [ i ] [ l ] suf[i][j]=\sum_{l=j}^{i} dp[i][l] suf[i][j]=l=jidp[i][l] ,也就是后缀和,那么式子就十分简单
s u f [ i ] [ j ] − s u f [ i ] [ j + 1 ] = s u f [ i − 1 ] [ j − 1 ] s u f [ i ] [ j ] = s u f [ i − 1 ] [ j − 1 ] + s u f [ i ] [ j + 1 ] suf[i][j]-suf[i][j+1]=suf[i-1][j-1]\\ suf[i][j]=suf[i-1][j-1]+suf[i][j+1] suf[i][j]suf[i][j+1]=suf[i1][j1]suf[i][j]=suf[i1][j1]+suf[i][j+1]

又是模拟走限制网格图,用卡塔兰数的套路。

于是我们可以预处理 O ( n ) O(n) O(n) ,然后 O ( 1 ) O(1) O(1) 地求出每一个我们需要的 d p [ i ] [ j ] dp[i][j] dp[i][j]

当我们求出所有 d p [ k − 1 ] [ j ] dp[k-1][j] dp[k1][j] 后,第 k k k 个数必须是 1 的限制,就告诉我们只能转移到 d p [ k ] [ j + 1 ] dp[k][j+1] dp[k][j+1] 。然后,第 k k k 位后面的数就比较自由了。此时 a a a 序列中间空出一段连续的位置可以填,我们再正着考虑,现在计数相当于要对于一个排列 a a a 任意地按 3 , 4 3,4 3,4 按钮,最后一下随便按一个结果等价。那么就是当下满足条件的排列 a a a 个数 ( j + ( n − k ) n − k ) {j+(n-k)\choose n-k} (nkj+(nk)) 再乘上按按钮方案数 2 max ⁡ ( 0 , n − k − 1 ) 2^{\max(0,n-k-1)} 2max(0,nk1)

时间复杂度 O ( n ) O(n) O(n)

CODE

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 500005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
//#define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
int MOD = 1;
int fac[MAXN<<1],inv[MAXN<<1],invf[MAXN<<1];
int C(int n,int m) {
	if(m < 0 || m > n) return 0;
	return fac[n] *1ll* invf[n-m] % MOD * invf[m] % MOD;
}
int dpsuf(int n,int m) {
	m = n-m;
	return (0ll+C(n+m,m) +MOD- C(n+m,m-1)) % MOD;
}
int pw[MAXN];
int main() {
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	n = read(); k = read(); MOD = read();
	fac[0]=fac[1]=inv[0]=inv[1]=invf[0]=invf[1]=1;
	for(int i = 2;i <= (n<<1);i ++) {
		fac[i] = fac[i-1] *1ll* i % MOD;
		inv[i] = (MOD - inv[MOD%i]) *1ll* (MOD/i) % MOD;
		invf[i] = invf[i-1] *1ll* inv[i] % MOD;
	}
	int ans = 0; pw[0] = 1;
	for(int i = 1;i <= n;i ++) pw[i] = pw[i-1]*2ll % MOD;
	if(k == 1) return AIput(pw[max(0,n-2)],'\n'),0;
	for(int i = 1;i < k;i ++) {
		int dp = (0ll+dpsuf(k-1,i) +MOD- dpsuf(k-1,i+1)) % MOD;
		ans = (0ll+ ans + dp*1ll*C(i+n-k,n-k)%MOD*pw[max(0,n-k-1)] % MOD) % MOD;
	}
	AIput(ans,'\n');
	return 0;
}
举报

相关推荐

0 条评论