非常好的期望套路
有一个长度为n的整数列\(a_1 , a_2 \dots a_n\) ,每个元素在\([1, x]\)中的整数中均匀随机生成。
• 有q个询问,第i个询问的结果是下标在\([l_i , r_i ]\)的元素的最小值。
•求这q个询问结果的最大值的期望\(,mod 666623333\)。
全概率公式\(E(x)=\sum_{i=0}^\infty P(x\geq i)\)
于是对于每一个\(i\),我们只要计算出\(P(ans\geq i)\)即可
然而因为这里要计算的是最小值,如果是大于等于很不方便,于是我们考虑转化为
\(1.P(ans\geq i)=1-P(ans<i)\) ,
也就是意味着每一个区间中都至少有一个数小于等于\(i-1\)
有一些区间相互包含,这里是求最小值的最大,转化为大的区间没用
如果一个数小于等于\(i-1\)那么它会对一些区间产生贡献,且这些区间的编号肯定是连续的。
\(2.\)考虑用点去覆盖区间,即每个点能覆盖一些区间,且覆盖的概率为\(p=\frac{i-1}{x},\)求所有区间都被覆盖的概率
设\(l[i]\)为\(i\)能覆盖到的最左边的区间,\(r[i]\)同理,\(f[i]\)为\(r[i]\)及其之前的区间都被覆盖的概率,那么有
\(f[i]=p*(\sum_{r[j]\geq l[i]-1} f[j]*(1-p)^{i-j-1})\)
\(3.\)即枚举上一个选的点\(j\),那么必须有\(r[j]\geq l[i]-1\)才能保证\(r[i]\)及其之前的区间都被覆盖,又因为\(j\)是上一个被选的,所以\([j+1,i-1]\)都没有被覆盖。
最后的答案就是\(\sum_{r[i]=m} f[i]*(1-p)^{n-i}\),就是枚举最后一个被选的点,然后因为它是最后一个,所以\([i+1,n]\)都没被覆盖
双指针可以优化到\(O(n)\)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
register LL x=0,f=1;register char c=getchar();
while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
return f*x;
}
const int MAXN=2005;
const int mod=666623333;
struct Node{
int l,r;
inline friend bool operator < (Node a,Node b){
if(a.l==b.l) return a.r>b.r;
return a.l<b.l;
}
}a[MAXN];
int st[MAXN],top;
int l[MAXN],r[MAXN],f[MAXN],invp[MAXN];
int n,m,x,ans;
inline int add(int x,int y){x+=y;return x>mod?x-mod:x;}
inline int dec(int x,int y){x-=y;return x<0?x+mod:x;}
inline int mul(LL x,int y){x*=y;return x>mod?x%mod:x;}
inline int qpow(int a,int b){
int res=1;
while(b){
if(b&1) res=mul(res,a);
a=mul(a,a);
b>>=1;
}
return res;
}
int main(){
n=read(),x=read(),m=read();
for(int i=1;i<=m;i++) a[i].l=read(),a[i].r=read();
sort(a+1,a+m+1);
for(int i=1;i<=m;i++){
while(top&&a[i].r<=a[st[top]].r) top--;
st[++top]=i;
}
m=top;
for(int i=1;i<=m;i++) a[i]=a[st[i]];
int head=1,tail=0;
for(int i=1;i<=n;i++){
while(tail<m&&a[tail+1].l<=i) tail++;
while(head<=tail&&a[head].r<i) head++;
l[i]=head,r[i]=tail;
}
for(int i=1;i<=x;i++){
int sum=1,p=mul(dec(i,1),qpow(x,mod-2)),
notp=dec(1,p),fp=qpow(notp,mod-2),leip=1;
invp[0]=1,f[0]=1;
for(int i=1;i<=n;i++) invp[i]=mul(invp[i-1],fp);
for(int i=1,j=0;i<=n;i++){
while(j<i&&r[j]<l[i]-1) sum=dec(sum,mul(f[j],invp[j])),j++;
f[i]=mul(sum,mul(leip,p)),leip=mul(leip,notp);
sum=add(sum,mul(f[i],invp[i]));//这样能够实现前缀和的优化
}
int tot=0;leip=1;
for(int i=n;i&&r[i]==m;i--,leip=mul(leip,notp))
tot=add(tot,mul(f[i],leip));
ans=add(ans,dec(1,tot));
}
printf("%d\n",ans);
}