其实这道题已经在我的学习笔记里面口胡过了…
但由于某种特殊原因…我写了一篇博客。
题面
Getting closer and closer to a mathematician, Serval becomes a university student on math major in Japari University. On the Calculus class, his teacher taught him how to calculate the expected length of a random subsegment of a given segment. Then he left a bonus problem as homework, with the award of a garage kit from IOI. The bonus is to extend this problem to the general case as follows.
You are given a segment with length lll . We randomly choose n n segments by choosing two points (maybe with non-integer coordinates) from the given segment equiprobably and the interval between the two points forms a segment. You are given the number of random segments nnn , and another integer kkk . The 2n2n2n endpoints of the chosen segments split the segment into (2n+1)(2n+1)(2n+1) intervals. Your task is to calculate the expected total length of those intervals that are covered by at least kkk segments of the nnn random segments.
You should find the answer modulo 998244353998244353998244353 .
翻译
有一段长为lll的线段,有nnn个区间,左右端点在[0,l)[0,l)[0,l)间均匀随机(可能不是整数)。
求期望被至少kkk段区间覆盖的长度,对998244353998244353998244353取膜。
来源:洛谷
题解
动态规划
首先将线段长度看成111,最后乘以lll即可。
利用某结论,随机选的2n2n2n端点在期望下可以被看成将线段划分为长度相等的2n+12n+12n+1段。
接下来我们考虑dp:
定义fi,jf_{i,j}fi,j?,gi,jg_{i,j}gi,j?分别为还未/已经存在一段覆盖次数不小于kkk,确定了前iii个端点,还有jjj个左端点未被匹配的方案数。
但是这样做显然有问题:一个方案可能有多个覆盖次数不小于kkk的段,因此我们还需要乘上覆盖次数。
不过有个更巧妙的做法:我们可以新增一个点(具体体现在下面的第三个转移方程),它在每次向fff转移到ggg的时候出现。
可以发现这样做等价于乘上一个系数。
然后列好转移方程就做完了:
fi,j?>fi+1,j+1,gi,j?>gi+1,j+1fi,j?>j×fi+1,j?1,gi,j?>j×gi+1,j?1fi,j?>gi+1,j(k≤j)f_{i,j}->f_{i+1,j+1},g_{i,j}->g_{i+1,j+1}\\ f_{i,j}->j\times f_{i+1,j-1},g_{i,j}->j\times g_{i+1,j-1}\\ f_{i,j}->g_{i+1,j}(k\leq j)\\ fi,j??>fi+1,j+1?,gi,j??>gi+1,j+1?fi,j??>j×fi+1,j?1?,gi,j??>j×gi+1,j?1?fi,j??>gi+1,j?(k≤j)
最终答案是:g2n+1,0×2n×n!2n!×(2n+1)\frac{g_{2n+1,0}\times 2^n\times n!}{2n!\times (2n+1)}2n!×(2n+1)g2n+1,0?×2n×n!?。
[注意乘的12n+1\frac{1}{2n+1}2n+11?是线段的平均长度]
复杂度:O(n2)O(n^2)O(n2)
积分+多项式
使用微元法,设离散变量x(0≤x≤1)x(0\leq x\leq 1)x(0≤x≤1),算完离散概率再用积分积起来。
那么一段区间覆盖该位置的概率就是2x×(1?x)2x\times (1-x)2x×(1?x)(乘2是因为左右端点可以交换)。
我们枚举kkk,可以很容易算出恰好覆盖kkk的次的概率:
E[X]=∫01∑m≤k(nk)(2x×(1?x))k×(1?2x×(1?x))n?kdxE[X]=\int_0^1 \sum_{m\leq k} \binom{n}{k}(2x\times (1-x))^k\times (1-2x\times (1-x))^{n-k} \mathrm{d}xE[X]=∫01?m≤k∑?(kn?)(2x×(1?x))k×(1?2x×(1?x))n?kdx
直接爆算是O(n3)O(n^3)O(n3)的。
用NTT优化乘法就是O(n2log?n)O(n^2\log n)O(n2logn)的了。
然而会被毒瘤出题人卡掉。
但是我们可以一开始就把多项式化为点值形式最后再还原。
这样就是O(n2)O(n^2)O(n2)了。
如果你定积分相当熟练地话…你可以利用Beta Function推式子,得到一个O(nlog?n)O(n\log n)O(nlogn)的方法。可以参考一下这位大佬的博客(我懒得写了)。
实现
太懒了…只有第一种方法。
/*Lower_Rating*/
/*probaility*/
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<vector>
#include<queue>
#include<bitset>
#include<set>
using namespace std;#define LL long long
#define DB double
#define MOD 998244353
#define Pr pair<LL,int>
#define X first
#define Y second
#define MAXN 5000
#define MAXM 400000
#define eps 1e-10
#define INF 1000000000
#define inf 100000000000000000LL
#define mem(x,p) memset(x,p,sizeof(x))LL read(){
LL x=0,F=1;char c=getchar();while(c<'0'||c>'9'){
if(c=='-')F=-1;c=getchar();}while(c>='0'&&c<='9'){
x=(x<<3)+(x<<1)+c-'0';c=getchar();}return x*F;
}
int add(int a,int b){
return (a+b>=MOD)?a+b-MOD:a+b;}
int dec(int a,int b){
return (a-b<0)?a-b+MOD:a-b;}
int mul(int a,int b){
return 1LL*a*b%MOD;}
int fst_pow(int a,int b){
int res=1;while(b){
if(b&1)res=mul(res,a);a=mul(a,a);b>>=1;}return res;
}
int inv(int a){
return fst_pow(a,MOD-2);}int n,l,k;
int dp[MAXN+5][MAXN+5][2],fac[MAXN+5],pw[MAXN+5];
int main()
{
n=read(),k=read(),l=read();fac[0]=pw[0]=1;for(int i=1;i<=2*n+1;i++)fac[i]=mul(fac[i-1],i),pw[i]=mul(pw[i-1],2);dp[0][0][0]=1;for(int i=0;i<=2*n;i++)for(int j=0;j<=i;j++){
if(j>=k)dp[i+1][j][1]=add(dp[i+1][j][1],dp[i][j][0]);for(int k=0;k<=1;k++){
if(j)dp[i+1][j-1][k]=add(dp[i+1][j-1][k],mul(dp[i][j][k],j));dp[i+1][j+1][k]=add(dp[i+1][j+1][k],dp[i][j][k]);}}printf("%d",mul(mul(mul(dp[2*n+1][0][1],pw[n]),mul(fac[n],inv(fac[2*n+1]))),l));
}