Luogu P3600 随机数生成器

有一个长度为 \(n\) 的整数序列 \(A\),每个数都在 \([1,x]\) 中随机生成。

给定 \(q\) 个区间 \([l_i,r_i]\),定义 \(f_i = \min\limits_{j=l_i}^{r_i} a_j\)。

求 \(t = \max\limits_{i=1}^{q} f_i\) 的期望,对 \(666623333\) 取模。

\(1 \leq n,x,q \leq 2000\)。


考虑 \(t=x+1-\sum\limits_{p=1}^{x} [t \leq p]\)。

也就是说,我们需要算出对于每个 \(p\),满足 \(t \leq p\) 的概率。

此时,对于每个 \(i\),一定有 \(f_i \leq p\)。也就是说,区间内一定存在一个 \(\leq p\) 的数。

我们考虑将 \(\leq p\) 的数设为 \(0\),\(> p\) 的数设为 \(1\),也就是说每个给出的区间内部要有 \(1\)。

考虑若最后序列中有 \(k\) 个 \(0\),则对应的方案有 \(p^k (x-p)^{n-k}\) 种,所以我们只需要考虑序列中 \(0\) 的个数即可。

接下来就可以愉快的 dp 了,令 \(dp_{i,j}\) 表示序列中有 \(j\) 个 \(0\) 且最后一个 \(0\) 在 \(i\) 的位置上,满足条件的方案数。

我们发现,此时所有左端点 \(\leq i\) 的区间,一定会被且需要被覆盖到。

我们不妨将所有区间按左端点从小到大排序,考虑 \(dp_{i_1,j}\) 能转移到 \(dp_{i_2,j}\),当且仅当对于左端点 \(\in (i_1,i_2]\) 的区间,都经过 \(i_2\)。

事实上这是容易预处理的,容易发现这样的 \(i_1\) 构成了一个区间,转移时可以使用前缀和优化。

你发现 \(dp\) 数组可以重复利用。令 \(n,q,x\) 同阶,复杂度 \(O(n^2)\)。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const long long mod=666623333;
const int N=2000;
struct Node{
	int l,r;
}a[N+10];
bool operator <(const Node &lhs,const Node &rhs){
	return lhs.l<rhs.l;
}
long long dp[N+10][N+10],sum[N+10][N+10],pow_mod[N+10][N+10];
void init(){
	for(int i=0;i<=N;i++){
		long long pre=1;
		for(int j=0;j<=N;j++){
			pow_mod[i][j]=pre;
			pre=pre*i%mod;
		}
	}
}
long long inv(long long num){
	long long pre=mod-2,ans=1;
	while(pre){
		if(pre&1){
			ans=ans*num%mod;
		}
		num=num*num%mod;
		pre>>=1;
	}
	return ans;
}
int main(){
	int n,x,q;
	scanf("%d %d %d",&n,&x,&q);
	init();
	for(int i=1;i<=q;i++){
		scanf("%d %d",&a[i].l,&a[i].r);
	}
	sort(a+1,a+1+q);
	int lst_pos=a[q].l;
	dp[0][0]=1;
	sum[0][0]=1;
	for(int i=1;i<=n;i++){
		int pre=0;
		for(int j=q;j>=1;j--){
			if(a[j].r<i){
				pre=a[j].l;
				break;
			}
		}
		for(int j=1;j<=n;j++){
			dp[i][j]=sum[i-1][j-1];
		}
		if(pre){
			for(int j=1;j<=n;j++){
				dp[i][j]-=sum[pre-1][j-1];
				dp[i][j]=(dp[i][j]%mod+mod)%mod;
			}
		}
		for(int j=0;j<=n;j++){
			sum[i][j]=(sum[i-1][j]+dp[i][j])%mod;
		}
	}
	long long ans=x+1;
	for(int i=1;i<=x;i++){
		long long pre=0;
		for(int j=0;j<=n;j++){
			pre+=(sum[n][j]-sum[lst_pos-1][j])*pow_mod[i][j]%mod*pow_mod[x-i][n-j]%mod;
			pre=(pre%mod+mod)%mod;
		}
		pre*=inv(pow_mod[x][n]);
		pre%=mod;
		ans-=pre;
		ans=(ans%mod+mod)%mod;
	}
	printf("%lld",ans);
    return 0;
}
posted @ 2026-01-03 23:24  Oken喵~  阅读(27)  评论(0)    收藏  举报