给⼀个⻓度为 \(l\) 的线段,随机选了 \(n\) 条⼦线段,求被覆盖了至少 \(k\) 次的期望⻓度。
\(1≤k≤n≤2000\)。
计数DP
https://jkloverdcoi.github.io/2019/04/16/CF1153/
随便在线段上钦定 \(2n\) 个点,分割成 \(2n+1\) 段区间,所以每段区间的期望长度就是 \(\frac l {2n+1}\)。于是只需要再乘上一段区间至少被 \(k\) 条线段覆盖的概率就好了。
设 \(f(i,j)\) 表示考虑 \(i\) 个端点,第 \(i\) 个端点后面的区间恰好被 \(j\) 条线段所覆盖的方案数。转移时枚举 \(i\) 是作为左端点还是右端点,\(O(n^2)\) 大力转移。
最后将所有合法方案数目求和,除以 \(f(2n,0)\) 得到概率。
统计答案时枚举各个区间的贡献,注意两边的DP组合起来的时候需要乘以 \(j!\),表示线段端点的对应关系。
CO int N=4000+10;
int fac[N];
int dp[N][N];
int main(){
int n=read<int>(),K=read<int>(),L=read<int>();
L=mul(L,fpow(2*n+1,mod-2));
fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
dp[0][0]=1;
for(int i=0;i<2*n;++i)
for(int j=n<i?n:i;j>=0;--j){
dp[i+1][j+1]=add(dp[i+1][j+1],dp[i][j]);
if(j) dp[i+1][j-1]=add(dp[i+1][j-1],mul(dp[i][j],j));
}
int ans=0;
for(int i=1;i<2*n;++i)
for(int j=K;j<=n;++j){
int sum=mul(dp[i][j],dp[2*n-i][j]);
sum=mul(sum,fac[j]); // corresponding relationship
ans=add(ans,sum);
}
ans=mul(ans,mul(L,fpow(dp[2*n][0],mod-2)));
printf("%d\n",ans);
return 0;
}
微积分
从另一种角度考虑,恰好 \(k\) 次的期望长度为
\(k\) 次 \(→k+1\) 次只需要乘上一个二次的,除以一个二次的。每次直接积分即可。
我发现积分的结果还要乘上 \(\binom{n}{k}\),这可能是因为线段其实是有放入的先后顺序的,即可以把它看成有标号的。
DP的做法应该是钦定了放入顺序就是从左到右。
poly operator*(CO poly&a,CO poly&b){
int n=a.size()-1,m=b.size()-1;
poly ans(n+m+1);
for(int i=0;i<=n;++i)for(int j=0;j<=m;++j)
ans[i+j]=add(ans[i+j],mul(a[i],b[j]));
return ans;
}
poly operator/(poly a,CO poly&b){
int n=a.size()-1,m=b.size()-1;
poly ans(n-m+1);
for(int i=n;i>=m;--i)if(a[i]){
ans[i-m]=mul(a[i],fpow(b[m],mod-2));
for(int j=i;j>=i-m;--j) a[j]=add(a[j],mod-mul(ans[i-m],b[j-(i-m)]));
}
return ans;
}
CO int N=4000+10;
int inv[N];
poly inter(CO poly&a){
int n=a.size()-1;
poly ans(n+2);
for(int i=1;i<=n+1;++i) ans[i]=mul(a[i-1],inv[i]);
return ans;
}
int calc(CO poly&a,int x){
int n=a.size()-1,ans=0;
for(int i=n;i>=0;--i) ans=add(mul(ans,x),a[i]);
return ans;
}
CO poly a=(poly){0,2,mod-2},b=(poly){1,mod-2,2};
int fac[N],ifac[N];
IN int binom(int n,int m){
return mul(fac[n],mul(ifac[m],ifac[n-m]));
}
int main(){
// freopen("CF1153F.in","r",stdin);
int n=read<int>();
inv[0]=inv[1]=1;
for(int i=2;i<=2*n+1;++i) inv[i]=mul(mod-mod/i,inv[mod%i]);
fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
ifac[n]=fpow(fac[n],mod-2);
for(int i=n-1;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
int K=read<int>(),L=read<int>();
poly f=(poly){1};
for(int i=1;i<=K;++i) f=f*a;
for(int i=1;i<=n-K;++i) f=f*b;
int ans=0;
for(int i=K;i<=n;++i){
poly g=inter(f);
ans=add(ans,mul(calc(g,1),binom(n,i)));
f=f/b*a;
}
ans=mul(ans,L);
printf("%d\n",ans);
return 0;
}
很遗憾我的代码会TLE。