牛客 7329 D - 火柴排队 (dp + 组合数)

链接: 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;
}

猜你喜欢

转载自blog.csdn.net/hddddh/article/details/108816071