题目
题意: 给你一个长度为 n 的 01序列,每次等概率随机交换序列中的一对数,如果它们是逆序对,则交换它们的位置,求排成顺序序列的期望操作次数。
思路: 最后的结果一定是0000011111这样,否则还有逆序对的。假设0的个数是cnt0,1的个数是cnt1。如果前cnt0个位置存在1,就说明他要交换掉。假设这个数量是k,说明最后边还有k个0和他们对应,都没有放到正确的位置。所有的选择方案是Cn2,其中有k*k的可能性选中一对能消去逆序对的。求和i从1到k,每次求选中一对能消去逆序对的两个位置的期望。可以发现是几何分布,前i-1次失败且第i次成功的概率,E(X) = 1/p,也就 i / Cn2的倒数,之后从1到k分别求和即可。
图片来自这位大佬的博客连接
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,k,T;
const int mod = 998244353;
ll qpow(ll a,int k)
{
ll res = 1;
while(k)
{
if(k&1) res = res * a % mod;
a = a * a % mod;
k >>= 1;
}
return res;
}
ll ni(ll x)
{
return qpow(x,mod-2);
}
void solve()
{
cin>>n;
int cnt0 = 0,cnt1 = 0;
vector<int> a(n);
for(int i=0;i<n;++i)
{
cin>>a[i];
if(!a[i]) cnt0++;
}
for(int i=0;i<cnt0;++i)
{
if(a[i]) cnt1++; //多少对不匹配
}
ll cc = 1ll*n*(n-1)/2; cc %= mod;
ll fenmu = ni(cc);
ll ans = 0;
for(int i=1;i<=cnt1;++i)
{
ll tmp = 1ll*i*i%mod*fenmu%mod;
tmp = ni(tmp);
ans = (ans + tmp) % mod;
}
cout<<ans<<"\n";
}
signed main(void)
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>T;
while(T--)
solve();
return 0;
}