题意:
长度为n的非递减序列,现已知其中m个位置上数的位置(一定知道首尾的数字),求还原该序列有多少种方案数,序列各位和的期望是多少?
分析:
每两个已知位置之间的问题相互独立,
则需要解决一个MIN>=A[I], MAX<=A[I+1]的非递减序列有多少种方案,序列和的期望是多少?
方案数:
因为N个数的非递减排列数只有一种,即这是一个组合问题。
可对应组合数学中“N种物品中选出K个,有多少种方案数”,C(N+K-1, N-1).
注:该模型用隔板法理解,将这K个物品通过插入N-1个隔板分成N份,即在最终的N+K-1个位置中选N-1个放上隔板。
期望:
所有方案数中每个可选数的出现次数是相同的,即满足A[I]到A[I+1]上的均匀分布,每位上的期望为(A[I]+A[I+1])/2;
那K个位置的期望为 k*(A[I]+A[I+1])/2;
组合数计算:
C(N,K)= N! / ( K! * ( N - K )! ),通过求逆元计算。
当P为素数时,a的逆元 = [ a ^ (p-2) ] % p
具体实现:
RE了一发,因为没有考虑组合数最大是N+K+1为底的,开成了10^6
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
#define mod 1000000009
using namespace std;
typedef long long LL;
LL a[2000],b[2000];
LL jc[2000100];
void init()
{
jc[0]=1;
for(int i=1;i<=2000010;i++)
jc[i]=jc[i-1]*i%mod;
}
LL quick_pow(LL n,LL m)
{
LL ans=1;
while(m>0)
{
if(m&1)
ans=(ans*n)%mod;
n=(n*n)%mod;
m>>=1;
}
return ans;
}
LL get_rev(LL n)
{
return quick_pow(n,mod-2);
}
LL cal(LL n,LL m)
{
return jc[n]*get_rev(jc[m])%mod*get_rev(jc[n-m])%mod;
}
int main()
{
LL n,m;
int T;
init();
scanf("%d",&T);
for(int t=1;t<=T;t++)
{
scanf("%lld %lld",&n,&m);
for(int i=0;i<m;i++)
scanf("%lld",&a[i]);
for(int i=0;i<m;i++)
scanf("%lld",&b[i]);
LL ca=1;
double sum=0;
for(int i=0;i<m-1;i++)
{
sum+=b[i];
LL num=b[i+1]-b[i]+1;
LL k=a[i+1]-a[i]-1;
if(k==0) continue;
ca=(ca*cal(num+k-1,min(k,n-1)))%mod;
sum+=(double)k*(b[i]+b[i+1])/2;
}
sum+=b[m-1];
printf("Case #%d: %lld %.3lf\n",t,ca,sum);
}
return 0;
}