牛客练习赛69 火柴排队 题解(dp)

题目链接

题目大意

给你一个长为n(n<=5e3)的数组a。随机使得k个元素增加d。要你求多大的概率使得,这些数组元素的相对大小不发生改变

输出 n 行每行一个整数,第 i 行的整数表示 k=i 时的答案

题目思路

看了题解觉得是一个不太难的dp,但有点难想到

\(dp[i][j][1]\) 表示为前i个元素有j个增加k,并且第i个元素加了k

\(dp[i][j][0]\) 表示为前i个元素有j个增加k,并且第i个元素吗没有加k

然后转移即可

代码

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#define fi first
#define se second
#define debug printf(" I am here\n");
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=5e3+5,inf=0x3f3f3f3f,mod=998244353;
const double eps=1e-10;
int n,d,a[maxn];
ll dp[maxn][maxn][2];
ll fac[maxn],finv[maxn];
ll qpow(ll a,ll b){
    ll ans=1,base=a;
    while(b){
        if(b&1){
            ans=ans*base%mod;
        }
        base=base*base%mod;
        b=b>>1;
    }
    return ans;
}
void init(){
    fac[0]=1;
    for(int i=1;i<=n;i++){
        fac[i]=fac[i-1]*i%mod;
    }
    finv[n]=qpow(fac[n],mod-2);
    for(int i=n-1;i>=0;i--){
        finv[i]=finv[i+1]*(i+1)%mod;
    }
}
ll cal(ll a,ll b){//注意是除以C(n,i)
    return finv[a]*fac[b]%mod*fac[a-b]%mod;
}
signed main(){
    scanf("%d%d",&n,&d);
    init();
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    sort(a+1,a+1+n);
    dp[0][0][0]=1;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=i;j++){
            if(a[i]>=a[i-1]+d){
                dp[i][j][0]=(dp[i-1][j][0]+dp[i-1][j][1])%mod;
            }else{
                dp[i][j][0]=dp[i-1][j][0]%mod;
            }
            if(j){// 防止re
                dp[i][j][1]=(dp[i-1][j-1][0]+dp[i-1][j-1][1])%mod;
            }
        }
    }
    for(int i=1;i<=n;i++){
        ll ans=(dp[n][i][0]+dp[n][i][1])*cal(n,i)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

posted @ 2020-09-26 15:19  hunxuewangzi  阅读(144)  评论(0编辑  收藏  举报