AtCoder Grand Contest 038E - Gachapon

\(\bf Description\)

一个 \(0\) 到 \(n-1\) 的随机数生成器,生成 \(i\) 的概率是 \(A_i/S\) ,其中 \(S=\sum_{i=0}^{n} A_i\) ,请你求出每个数出现次数 \(\geq B_i\) 的期望次数。

\(\bf Solution\)

什么生成函数爆推的做法一点不会啊……

min-max容斥,考虑每个集合最早出现出现次数 \(\geq B_i\) 的数的期望时间,由期望可加性,就是所有数 \(<B_i\) 的局面的期望出现次数之和。

对于一个集合,下一步跳出它的概率 \(P=\frac{s}{S}\) ,\(s\) 是集合中的 \(A_i\) 之和。如果我们知道它出现的概率是 \(p\) ,那么它存在的期望次数就是 \(\frac{p}{P}\) 。

然后考虑一下 \(p\) 这个东西怎么算,假如现在已经生成的数的概率为 \(t_1\) 到 \(t_m\) ,个数是 \(x_1\) 到 \(x_m\) ,且设 \(X\) 为总和,那么可得(对这个柿子还是有点困惑啊……懂,但是自己推就是错的,很自闭)
\[ p=X! \prod_{i=1}^{m} \left( \frac{t_i}{s} \right)^{x_i} \frac{1}{x_i!} =\frac{X!}{s^X} \prod_{i=1}^m \frac{t_i^{x_i}}{x_i!} \]
\(f_{i,j,k}\) 表示前 \(i\) 个数,\(X=j\) ,\(s=k\) 的贡献(所谓的贡献,是容斥之后的贡献,并且dp的时候只算 \(\prod\) 后面那一部分),然后背包一下就好了。

然后我开始写,然后我又算不清复杂度了……为什么最近老这样……

有个坑是当前这个数就算是0个,那也和不在集合是不一样的……然后我还把 \(\frac{1}{P}\) 弄成 \(P\) 了……

由于太懒了,所以就很不优雅地for到400了……

#include<bits/stdc++.h>
#define ll long long
#define fr(i,x,y) for(int i=(x);i<=(y);i++)
#define rf(i,x,y) for(int i=(x);i>=(y);i--)
#define frl(i,x,y) for(int i=(x);i<(y);i++)
using namespace std;
const int N=404;
const int p=998244353;
int n,a[N],b[N];
ll f[N][N];

void read(int &x){ scanf("%d",&x); }

ll qpow(ll sum,ll n){
    ll ans=1;
    for(;n;n>>=1,sum=sum*sum%p) if (n&1) ans=ans*sum%p;
    return ans;
}

ll mul[N],inv[N];
void init(){
    mul[0]=1;
    frl(i,1,N) mul[i]=mul[i-1]*i%p;
    inv[N-1]=qpow(mul[N-1],p-2);
    rf(i,N-2,0) inv[i]=inv[i+1]*(i+1)%p;
}

void Add(ll &x,ll y){
    x+=y;//x%=p;
    if (x<0) x+=p;
    if (x>=p) x-=p;
}

int main(){
    init();
    read(n);
    fr(i,1,n) read(a[i]),read(b[i]);
    int S=0;
    fr(i,1,n) S+=a[i];
    //S=qpow(S,p-2);
    f[0][0]=p-1;
    fr(i,1,n)
     rf(k,400,0)
      fr(j,0,400)
       frl(x,0,b[i])
        if (f[j][k]) Add(f[j+x][k+a[i]],p-f[j][k]*qpow(a[i],x)%p*inv[x]%p);
    ll ans=0;
    fr(j,0,400)
     fr(k,1,400)
      Add(ans,mul[j]*f[j][k]%p*qpow(k,p-1-j)%p*qpow(k,p-2)%p*S%p);
    cout<<ans<<endl;
    return 0;
}
上一篇:NOIP2018DAY1铺设道路(贪心)


下一篇:KNN-综合应用