[UR #19]通用测评号

通用测评号

题解

首先,我们可以将原题目进行一个转化,就是我们可以对所有的数随机增加,不再限制只加没满的点,同时答案统计的也是所有 ⩾ n \geqslant n n的数的期望个数。
显然,当一个数大于 a a a后,即使我们再在上面增加,也依然不会它当前的状态,也就是说,这是一个自环,不会影响到从当前点流出的概率。

接下来,我们就可以去考虑单个点产生的贡献。
一个点的填满产生贡献,当且仅当它被填满时还有点没有被填到 b i b_i bi,事实上其他点被填到 b i b_i bi后怎么填,我们是根本不需要管的,因为它根本不影响我们计算当前点的贡献。
我们只考虑有其他点在填到 b i b_i bi之前的填法与这个点填满之前的填法的概率。
我们定义 d p i , j dp_{i,j} dpi,j表示在前 i i i个操作中,已经填满了 j j j个人,并且我们当前计算的这个人还没有被填满的概率。
转移的方程比较显然,一种是当前这个操作没能让任何人填满,直接乘当前概率即可。
另一种就是它让一个人终结了,我们就去再前面没有确定的操作中选出 b − 1 b-1 b1个即可。
显然,最后我们得到的 ( n − 1 ) ! d p ( n − 1 ) b + a − 1 , n − 1 (n-1)!dp_{(n-1)b+a-1,n-1} (n1)!dp(n1)b+a1,n1就是不行的答案,用 1 1 1减去即可得到它的贡献。

时间复杂度 O ( n 3 ) O\left(n^3\right) O(n3)

源码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pii;
#define MAXN 62505
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lowbit(x) (x&-x)
const int mo=998244353;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=200000;
const int INF=0x3f3f3f3f;
const double LOG310=log(10)/log(3);
const double Pi=acos(-1.0);
const double eps=1e-9;
const int orG=3,ivG=332748118;
const int lm=10;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
    _T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
    x*=f;
}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,a,b,dp[MAXN][255],fac[MAXN],inv[MAXN],ff[MAXN],ans;
void init(){
    fac[0]=fac[1]=inv[0]=inv[1]=ff[1]=1;
    for(int i=2;i<=n*a;i++){
        fac[i]=1ll*i*fac[i-1]%mo;
        ff[i]=1ll*(mo-mo/i)*ff[mo%i]%mo;
        inv[i]=1ll*ff[i]*inv[i-1]%mo;
    }
}
int C(int x,int y){
    if(x<0||y<0||x<y)return 0;
    return 1ll*fac[x]*inv[y]%mo*inv[x-y]%mo;
}
int main(){
    read(n);read(a);read(b);init();dp[0][0]=1;
    for(int i=0;i<(n-1)*b+a-1;i++)
        for(int j=0;j<n&&j*b<=i;j++)if(dp[i][j]){
            int tmp=1ll*ff[n-j]*dp[i][j]%mo;
            Add(dp[i+1][j],tmp,mo);
            Add(dp[i+1][j+1],1ll*C(i-j*b,b-1)*tmp%mo,mo);
        }
    ans=add(n,mo-1ll*fac[n]*dp[(n-1)*b+a-1][n-1]%mo,mo)%mo;
    printf("%d\n",ans);
    return 0;
}

谢谢!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值