随机数生成器

随机数生成器

有一长度为n的序列,序列每个位置等概率出现([1,x])的整数,有q个询问,询问区间([l,r])的最小值,现在要求q个询问的结果的最大值的期望(mod 666623333),1≤n,x,q≤2000。

显然问题的结果必然为分数形式,而总方案确定(x^n),所以每种情况下概率确定,我们只要想办法统计方案数即可,接下来变为组合计数问题。

于是设(A[i])表示最大值为i的方案数,显然不好求,因为一旦你限定一些结果为i,必然剩下的询问结果都必须小于i,而接下来划分问题就会发生重复,于是不得不用容斥原理,而显然数据范围不接受,而且很麻烦。

考虑更换状态,利用拆分,设(A[i])表示最大值小于等于i的方案数,不难得知,此时要让询问结果的最大值小于等于i,即所有区间的最小值都要小于等于i,而对于这个问题,我们只要限定覆盖区间内的一些位置必然小于i即可,于是设(B[i])表示用i个点覆盖所有区间的方案数,于是

[A[i]=sum_{j=1}^nB[j]j^i(n-j)^{x-i} ]

现在问题在于求(B[i]),于是设(C[i][j])表示填到第i个位置,有j个已经填了,覆盖所有区间的方案数,并设(fl[i],fr[i])分别表示覆盖第i个位置最左边的区间,最右边的区间,特别地当第i个位置不被任何区间覆盖,分别表示最靠近的左边的区间和最右边的区间。

于是不难得知

[C[i][j]=sum_{fl[k]+1geq fr[i]}C[k][j-1] ]

而有注意到用于转移的状态为连续的,考虑前缀和优化,故时间复杂度为(O(n^2))

于是求出要求的所有式子,答案就应为

[ans=frac{sum_{i=1}^x(A[i]-A[i-1]) imes i}{x^n} ]

以此求出逆元进行计算即可。

参考代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#define il inline
#define ri register
#define ll long long
#define yyb 666623333
using namespace std;
struct interval{
    int l,r;
    il bool operator<(interval&x){
        return (l==x.l)?(r>x.r):(l<x.l);
    }
}I[2001],s[2001];int top;
ll g[2001][2001],opt[2001][2001],ans,
    h[2001],p[2001];int fl[2001],fr[2001];
il void read(int&);
il ll frac(ll,ll),pow(ll,ll);
int main(){
    int n,x,q,i,j,k;
    read(n),read(x),read(q);
    for(i=1;i<=q;++i)read(I[i].l),read(I[i].r);
    sort(I+1,I+q+1);
    for(i=1;i<=q;++i){
        while(top&&s[top].r>=I[i].r)--top;
        s[++top]=I[i];
    }q=top;
    for(i=1;i<=n;++i){
        for(j=1;j<=q;++j)if(s[j].r>=i)break;fl[i]=j;
        for(j=q;j>=1;--j)if(s[j].l<=i)break;fr[i]=j;
    }g[0][0]=opt[0][0]=1;
    for(i=1;i<=n;++i){
        k=fl[i]-1;
        for(j=1;j<=n;++j)
            g[i][j]=(opt[i-1][j-1]-(k?opt[s[k].l-1][j-1]:0))%yyb;
        for(j=0;j<=n;++j)opt[i][j]=(opt[i-1][j]+g[i][j])%yyb;
    }
    for(i=s[q].l;i<=n;++i)
        for(j=1;j<=n;++j)(h[j]+=g[i][j])%=yyb;
    for(i=1;i<=x;++i)
        for(j=1;j<=n;++j)
            (p[i]+=h[j]*pow(i,j)%yyb*pow(x-i,n-j))%=yyb;
    for(i=1;i<=x;++i)(ans+=(p[i]-p[i-1])*i)%=yyb;
    (ans+=yyb)%=yyb,printf("%lld
",frac(ans,pow(x,n)));
    return 0;
}
il ll frac(ll a,ll b){
    return a*pow(b,yyb-2)%yyb;
}
il ll pow(ll x,ll y){
    ll ans(1);while(y){
        if(y&1)(ans*=x)%=yyb;
        (x*=x)%=yyb,y>>=1;
    }return ans;
}
il void read(int &x){
    x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}

原文地址:https://www.cnblogs.com/a1b3c7d9/p/10852657.html