正题
题目链接:https://www.luogu.com.cn/problem/P3600
题目大意
nnn个数的序列,每个数是[1..x][1..x][1..x]中的一个,有qqq个区间[l..r][l..r][l..r],求所有区间最小值的最大值的期望。
解题思路
首先如果一个区间包含别的区间,那么这个区间显然不需要考虑,所以去掉后左端点和右端点同时递增,考虑最大值不超过iii的方案hih_ihi,定义gig_igi表示放iii个点,每个区间至少包括一个点的方案。
有hi=gj∗ij∗(x−i)n−jh_i=g_j*i^j*(x-i)^{n-j}hi=gj∗ij∗(x−i)n−j
然后fi,jf_{i ,j}fi,j表示前iii个位置,iii有点,总共放了jjj个点的方案,考虑转移,k−>jk->jk−>j时要保证k∼ik\sim ik∼i这个区间内没有一个完整的区间,预处理好后有fi,j=∑fk,j−1f_{i,j}=\sum f_{k,j-1}fi,j=∑fk,j−1
我们发现kkk的取值是一段区间,前缀和优化即可。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=2100,XJQ=666623333;
struct node{ll l,r;
}a[N],b[N];
ll n,x,q,f[N][N],s[N][N],fl[N],fr[N],h[N],g[N],tot,ans;
bool cmp(node x,node y)
{return (x.l==y.l)?(x.r<y.r):(x.l<y.l);}
ll power(ll x,ll b){ll ans=1;while(b){if(b&1)ans=ans*x%XJQ;x=x*x%XJQ;b>>=1;}return ans;
}
int main()
{scanf("%lld%lld%lld",&n,&x,&q);for(ll i=1;i<=q;i++)scanf("%lld%lld",&b[i].l,&b[i].r);sort(b+1,b+1+q,cmp);for(ll i=1;i<=q;i++){if(i>1&&b[i].l==b[i-1].l)continue;while(tot&&a[tot].r>=b[i].r)tot--;a[++tot]=b[i];}memset(fl,0x3f,sizeof(fl));memset(fr,0xcf,sizeof(fr));for(ll i=1;i<=tot;i++) for(ll j=a[i].l;j<=a[i].r;j++)fl[j]=min(fl[j],i),fr[j]=max(fr[j],i);ll l=0;fl[0]=fr[0]=0;for(int i=1;i<=n;i++)if(fr[i]<0)fl[i]=l+1,fr[i]=l;else l=max(l,fr[i]);l=0;f[0][0]=s[0][0]=1;for(ll i=1;i<=n;i++){while(l<i-1&&fr[l]+1<fl[i])l++;for(int j=1;j<=i;j++)if(l)f[i][j]=(s[i-1][j-1]-s[l-1][j-1]+XJQ)%XJQ;else f[i][j]=s[i-1][j-1];for(int j=0;j<=i;j++)s[i][j]=(s[i-1][j]+f[i][j])%XJQ;}for(ll i=1;i<=n;i++)if(fr[i]==tot)for(ll j=1;j<=n;j++)(g[j]+=f[i][j])%=XJQ;for(ll i=1;i<=x;i++)for(ll j=1;j<=n;j++)h[i]=(h[i]+g[j]*power(i,j)%XJQ*power(x-i,n-j)%XJQ)%XJQ;ll z=power(power(x,n),XJQ-2);for(ll i=n;i>=1;i--)h[i]=(h[i]-h[i-1]+XJQ)%XJQ;for(ll i=1;i<=n;i++)ans=(ans+h[i]*i%XJQ*z%XJQ)%XJQ;printf("%lld",ans);
}