题意:
这是一个递归程序。
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
思路:
首先考虑一个长度为n的全排列期望产生多少逆序对
n长序列则最多有组逆序对(一个正序数列 1 2 3 4 5 每次从后面拿一位放在前面任意一个位置都能凑成逆序列 一共有有0~n-1的前n项和个), 其中每对出现的概率为1/2, 所以期望为
然后考虑在n长序列中取m长子序列的长度的概率是多少
因为有种取法(总情况), 在n中取m个有
种取法, 所以
考虑f(n)表示n长序列的贡献
边界
那么先加上其全排列的贡献:
所以得出式子:
移项得:
最后答案就是:
#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
const int N = 3000 + 5;
const int MOD = 998244353;
const int mod = 998244353;
ll comb[N][N];
void init(){
for(int i = 0; i < N; i ++){
comb[i][0] = comb[i][i] = 1;
for(int j = 1; j < i; j ++){
comb[i][j] = comb[i-1][j] + comb[i-1][j-1];
comb[i][j] %= MOD;
}
}
}
ll f[N];
ll two[N];
ll inv2,inv4;
ll power(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
{
ans=ans*a%mod;
}
a=a*a%mod;
b>>=1;
}
return ans;
}
int g(int x){
if(x==0)return 0;
int re=x*(x+mod-1)%mod;
re=re*inv4%mod;
return re;
}
int main()
{
init();
f[0]=0;
f[1]=0;
two[0]=1;
inv2=power(2,mod-2);
inv4=power(4,mod-2);
for(int i=1;i<=N;i++)
{
two[i]=two[i-1]*inv2%mod;
}
for(int i=2;i<=N;i++)
{
f[i]=i*(i-1)%mod*inv4%mod;
for(int j=2;j<i;j++)
{
/*ll tmp=f[j];
tmp=tmp*comb[i][j]%mod;
tmp=tmp*two[i]%mod;
f[i]=(f[i]+tmp)%mod;*/
f[i]=(f[i]+comb[i][j]*f[j]%mod*two[i]%mod)%mod;
}
//f[i]=(f[i]*power(1-two[i]));
f[i]=(f[i]*power((1-two[i]+mod)%mod,mod-2))%mod;
}
ll n;
while(cin>>n)
{
ll ans=0;
for(int i=2;i<=n;i++)
{
ans=(ans+f[i])%mod;
}
ans=ans*power(n,mod-2)%mod;
cout<<ans<<endl;
}
return 0;
}