链接: D - 火柴排队
题意:
给一个长度为 n 的数组 , 使其中 k 个元素的值增加 x , 分别求出 k 等于 1 - n 时这 n个数的相对大小不变的概率 。
思路:
其实就是求 有 k 个元素增加 x 的方案数 , 不难想到用 dp。
dp[ i ][ j ][1] 表示前 i 个 有 j 个数增加了 x,并且第 i 个数增加了 x.
dp[ i ][ j ][0] 表示前 i 个 有 j 个数增加了 x,并且第 i 个数没有增加.
然后转移就好了 , 当前加x可以由上一个加或者不加转移 , 当前不加x就要判断一下 去过a[i-1] + x <= a[i+1],那么就能由两个转移,否则只能由上一个不加转移。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e3+7;
const int mod = 998244353;
ll n,k;
int dp[maxn][maxn][3],a[maxn],ans[maxn];
ll a1[maxn],b1[maxn];
ll quickpow(ll a ,ll b){
ll ans=1;
while(b>0){
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
ll ni(ll x){
return quickpow(x,mod-2);
}
void init (){
a1[0]=1;
for(int i=1;i<maxn;i++){
a1[i]=(a1[i-1]*i)%mod;
}
b1[maxn-1]=ni(a1[maxn-1]);
for(int i=maxn-2;i>=0;i--){
b1[i]=b1[i+1]*(i+1)%mod;
}
}
ll c(ll n,ll m){
return (a1[n]*b1[m]%mod)*b1[n-m]%mod;
}
int main() {
scanf("%lld%lld",&n,&k);
init();
for(int i = 1; i <= n; i ++){
scanf("%lld",&a[i]);
}
sort(a+1,a + n + 1);
dp[1][0][0] = 1;
dp[1][1][1] = 1;
for(int i = 2 ; i <= n; i ++){
for(int j = 0; j <= i; j ++){
dp[i][j][0] = dp[i-1][j][0];
if(a[i-1] + k <= a[i]){
dp[i][j][0] = (dp[i][j][0] + dp[i-1][j][1]) % mod;
}
if(j == 0) continue;
dp[i][j][1] =( dp[i-1][j-1][1] + dp[i-1][j-1][0]) % mod;
}
}
for(int i = 1; i <= n; i ++){
ans[i] = (dp[n][i][0] + dp[n][i][1] )% mod;
}
for(int i = 1; i <= n; i ++){
ans[i] = ans[i] * quickpow(c(n, i) ,mod - 2) % mod;
printf ("%d\n",ans[i]);
}
return 0;
}