牛客练习赛69 D. 火柴排队

链接

https://ac.nowcoder.com/acm/contest/7329/D

题意

长度为 $n$ 序列 $a$ 中找出 $k$ 个数加 $d$,使新序列中如果 $a’_i>a’_j$,那么原序列也 $a_i>a_j$ 的概率。

对于每个 $1\le k\le n$,你都要输出其对应的答案,答案模 $998244353$。

思路

等价于一个单调上升的序列选 $k$ 个数加 $d$ 后仍单调上升的概率。

记 $dp[i][j][0/1]$ 为前 $i$ 个数选了 $j$ 个数加上 $d$ 且当前数是否选择,满足题意的方案数:

边界:$dp[1][0][0]=dp[1][1][1]=1$

状态转移方程:

$\begin{cases}
dp[i][j][0]=dp[i-1][j][0]+dp[i-1][j][1]*(a[i-1]+d>a[i])\
dp[i][j][1]=dp[i-1][j-1][0]+dp[i-1][j-1][1]
\end{cases}$

$j$ 从 $0$ 开始。

$\frac{dp[n][k][0]+dp[n][k][1]}{\binom{n}{k}}$ 就是答案。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <bits/stdc++.h>
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(),(x).end()
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
using namespace std;
typedef double DB;
typedef long long LL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;
//head
const int N=5005;
const LL MOD=998244353;
int n,d,a[N];
LL fac[N],invf[N],dp[2][N][2];
LL fpow(LL a,LL b) {
LL ret=1;
while(b) {
if(b&1) ret=ret*a%MOD;
a=a*a%MOD;
b>>=1;
}
return ret;
}
int main() {
//freopen("D:/Sublime Text 3/in.txt","r",stdin);
scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n);
dp[1][1][1]=dp[1][0][0]=1;
for(int i=2;i<=n;i++)
for(int j=0;j<=i;j++) {
dp[i&1][j][0]=(dp[(i-1)&1][j][0]+dp[(i-1)&1][j][1]*(a[i-1]+d<=a[i]))%MOD;
if(j>=1) dp[i&1][j][1]=(dp[(i-1)&1][j-1][0]+dp[(i-1)&1][j-1][1])%MOD;
}
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
invf[n]=fpow(fac[n],MOD-2);
for(int i=n-1;i>=1;i--) invf[i]=invf[i+1]*(i+1)%MOD;
for(int i=1;i<=n;i++) printf("%lld\n",(dp[n&1][i][0]+dp[n&1][i][1])%MOD*fac[i]%MOD*fac[n-i]%MOD*invf[n]%MOD);
return 0;
}