Problem Description
一天,Y_UME得到一個整數N和一個有趣的程序,如下圖所示:
大致意思:
這是一個遞歸程序。
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)\)
所以得出柿子:
移項得:
最后答案就是:
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;
}