背景
题面
题解
我们先不考虑第 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[i−1][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=j−1∑i−1dp[i−1][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[i−1][j−1]suf[i][j]=suf[i−1][j−1]+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[k−1][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} (n−kj+(n−k)) 再乘上按按钮方案数 2 max ( 0 , n − k − 1 ) 2^{\max(0,n-k-1)} 2max(0,n−k−1) 。
时间复杂度 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;
}