有一长度为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();
}