Serval and Bonus Problem

给⼀个⻓度为 \(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\) 次的期望长度为

\[l\times \int_0^1 (2x(1-x))^k (1-2x(1-x))^{n-k} dx \]

\(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。