Everything Is Generated In Equal Probability(概率与期望)

题目地址

Problem Description

一天,Y_UME得到一个整数N和一个有趣的程序,如下图所示:

img

大致意思:

这是一个递归程序。

1.答案加上数组中逆序对的个数。

2.对数组等概率地取一个子序列(可以为空序列)

3.递归计算子序列, 并把结果加到答案中

4.返回答案

Y_UME想玩这个程序。首先,随机生成一个等概率的整数n∈[1,n]。然后他随机生成一个长度为n的排列,概率相等。然后,他运行有趣的程序(function calculate()),将这个排列作为参数,然后获得一个返回值。请输出此值的期望值998244353。

长度n的置换是长度n的数组,其中只包含一对不同的整数∈[1,n]。

置换p中的反转对是一对索引(i,j),使得i>j和<pj。例如,一个置换[4,1,3,2]包含4个逆序:(2,1),(3,1),(4,1),(4,1),(4,3)。

在数学中,子序列是一个序列,它可以从另一个序列中派生出来,方法是删除一些或不删除元素,而不改变其余元素的顺序。注意,空子序列也是原始序列的子序列。

为了更好地理解,请参考https://en.wikipedia.org/wiki/ence

Input

有多个测试用例。

每种情况都以包含一个整数N(1≤N≤3000)的行开始。

保证所有测试用例的N之和不大于5×104。

Output

对于每个测试用例,输出一行包含一个表示答案的整数。

Sample Input

1
2
3

Sample Output

0
332748118
554580197

Solution

首先考虑一个长度为n的全排列期望产生多少逆序对

n长序列则最多有(frac{n(n-1)}{2})组逆序对, 其中每对出现的概率为(frac12), 所以期望为(frac{n(n-1)}{4})

然后考虑在n长序列中取m长子序列的长度的概率是多少

因为有(2^n)种取法, 在n中取m个有(C^m_n)种取法, 所以(P=frac{C^m_n}{2^n})

考虑f(n)表示n长序列的贡献

边界(f(0)=f(1)=0)

那么先加上其全排列的贡献: (f(n)+=frac{n(n-1)}{4})

再加上其子序列对其贡献: (f(n)+=sumlimits_{m=0}^{n}frac{C^m_n}{2^n}f(m))

所以得出柿子:

[f(n)=frac{n(n-1)}{4}+sumlimits_{m=0}^{n}frac{C^m_n}{2^n}f(m) ]

移项得:

[f(n)=frac{sumlimits_{m=0}^{n-1}frac{C^m_n}{2^n}f(m)+frac{n(n-1)}{4}}{1-frac{1}{2^n}} ]

最后答案就是:

[ans=frac{sumlimits_{i=0}^{N}f(i)}{N} ]

Code

#include<iostream>
#define int unsigned long long
using namespace std;
const int N=4e3+28,p=998244353;
int Pow(int x,int y=p-2){
    int re=1;
    while(y){
	if(y&1)re=re*x%p;
	x=x*x%p;
	y>>=1;
    }
    return re;
}
int mul[N],inv[N],two[N];
int inv4;
int pre(){
    mul[0]=inv[1]=two[0]=1;
    for(int i=1;i<=4000;i++)mul[i]=mul[i-1]*i%p;
    for(int i=0;i<=4000;i++)inv[i]=Pow(mul[i]);
    int inv2=Pow(2);
    inv4=inv2*inv2%p;
    for(int i=1;i<=4000;i++)two[i]=two[i-1]*inv2%p;
}
int C(int n,int m){
    int re=mul[n];
    re=re*inv[m]%p;
    re=re*inv[n-m]%p;
    return re;
}
int g(int x){
    if(x==0)return 0;
    int re=x*(x+p-1)%p;
    re=re*inv4%p;
    return re;
}
int f[N];
signed main(){
    pre();
    for(int i=2;i<=4000;i++){
	f[i]=g(i);
	for(int j=2;j<i;j++){
	    int tmp=f[j];
	    tmp=tmp*C(i,j)%p;
	    tmp=tmp*two[i]%p;
	    f[i]=(f[i]+tmp)%p;
	}
	f[i]=f[i]*Pow(1+p-two[i])%p;
    }
    int n;
    while(scanf("%lld",&n)!=EOF){
	int ans=0;
	for(int i=2;i<=n;i++)ans=(ans+f[i])%p;
	ans=ans*Pow(n)%p;
	printf("%lld
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/nlKOG/p/11243080.html