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)+=\sum\limits_{m=0}^{n}\frac{C^m_n}{2^n}f(m)\)

所以得出柿子:

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

移項得:

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

最后答案就是:

\[ans=\frac{\sum\limits_{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\n",ans);
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM