Luogu P3600 随机数生成器

Luogu P3600 随机数生成器

题目描述

sol研发了一个神奇的随机数系统,可以自动按照环境噪音生成真·随机数。

现在sol打算生成\(n\)\([1,x]\)的整数\(a_1...a_n\),然后进行一些询问。

\(q\)次询问,每次询问i有两个参数\(li\)\(ri\),sol会计算\(\min_{li \leq j \leq ri} a_j\)(a数组中下标在\(li、ri\)之间的数的最小值)。

最后测试结果会是这些询问得到的结果的最大值。

sol进行了很多次实验,现在他想问问你测试结果的期望大小是多少,对\(666623333\)取模。

输入输出格式

输入格式:

第一行三个数\(n、x、q\)

下面q行,第i行两个数表示\(li\)\(ri\)

输出格式:

一行一个数,表示答案。

输入输出样例

输入样例#1:

2 2 1
1 2

输出样例#1:

499967501

输入样例#2:

6 6 6
1 3
2 4
3 5
4 6
5 6
3 4

输出样例#2:

88571635


根据整数期望公式:
\[ \begin{align} ans&=\sum_{i=1}^xi*p(X=i)\\ &=\sum_{i=1}^xp(X\geq i)\\ &=\sum_{i=1}^x1-p(X<i) \end{align} \]
因为是所有的最小值取最大值,所以\(p(X<i)\)\(p(X\geq i)\)好求一些。

我们考虑对每个\(i\)分别求。

合法情况要求询问的每个区间中至少有一个\(<i\)的数。如果两个区间有包含关系,很明显大的区间没有用。将剩余的区间按左端点排序后,对于坐标上的每个点,包含它的区间的编号一定是连续的。所以我们将问题转换:有很多区间,选择一个区间的概率为\(P\),要选择一些区间将整个坐标轴覆盖,问概率。

很明显这些区间的左右端点都是单调不下降的(去掉无用区间)。设\(f_i\)表示最后一个选择的区间为\(i\),并且将\(r_i\)以及之前的坐标都覆盖了的概率。
\[ f_i=\sum_{r_j\geq l_i-1}f_j*P*(1-P)^{i-j-1} \]
前缀和优化一下单次\(DP\)就可以做到\(O(n)\)了。

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 2005

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

const ll mod=666623333;
ll ksm(ll t,ll x) {
    ll ans=1;
    for(;x;x>>=1,t=t*t%mod)
        if(x&1) ans=ans*t%mod;
    return ans;
}

int n,x,m;
struct interval {
    int l,r;
    bool operator <(const interval &a)const {
        if(l!=a.l) return l<a.l;
        return r>a.r;
    }
}s[N];

interval st[N];
int top;

int L[N],R[N];
ll ans;
ll DP(ll p) {
    if(p==1) return 1;
    static ll f[N],pw[N],inv[N];
    memset(f,0,sizeof(f));
    
    pw[0]=inv[0]=1;
    for(int i=1;i<=n;i++) pw[i]=pw[i-1]*(mod+1-p)%mod;
    inv[1]=ksm(mod+1-p,mod-2);
    for(int i=2;i<=n;i++) inv[i]=inv[i-1]*inv[1]%mod;
    f[0]=1;
    int lx=0,rx=0;
    ll sum=1;
    for(int i=1;i<=n;i++) {
        while(lx<=i&&R[lx]<L[i]-1) {
            sum=(sum-f[lx]*inv[lx]%mod+mod)%mod;
            lx++;
        }
        while(rx+1<i&&R[rx+1]>=L[i]-1) {
            sum=(sum+f[rx+1]*inv[rx+1])%mod;
            rx++;
        }
        f[i]=sum*p%mod*pw[i-1]%mod;
    }
    ll ans=0;
    for(int i=1;i<=n;i++) {
        if(R[i]==m) (ans+=f[i]*pw[n-i])%=mod;
    }
    return ans;
}

int main() {
    n=Get(),x=Get(),m=Get();
    for(int i=1;i<=m;i++) s[i].l=Get(),s[i].r=Get();
    sort(s+1,s+1+m);
    for(int i=1;i<=m;i++) {
        while(top>=1&&st[top].r>=s[i].r) top--;
        st[++top]=s[i];
    }
    
    m=top;
    for(int i=1;i<=m;i++) s[i]=st[i];
    
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) if(s[j].l<=i&&i<=s[j].r) R[i]=j;
        for(int j=m;j>=1;j--) if(s[j].l<=i&&i<=s[j].r) L[i]=j;
    }
    top=0;
    for(int i=1;i<=n;i++) {
        if(L[i]&&R[i]) L[++top]=L[i],R[top]=R[i];
    }
    n=top;
    ll invx=ksm(x,mod-2);
    for(int i=1;i<=x;i++) {
        (ans+=mod+1-DP((i-1)*invx%mod))%=mod;
    }
    cout<<ans;
    return 0;
}

转载于:https://www.cnblogs.com/hchhch233/p/11085314.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值