题目大意:有一个长度为n的只含有0和1的数组,要求把数组按从小到大排序,但每次操作只能随机选取一前一后两个位置并交换这两个位置上的数,问期望的交换次数是多少
1<=n<=2e5.
思路:首先,从n个数中随机选两个,一共有c(n,2)种可能,然后排完序后的数组是形如00111这样的,所以我们统计数组0的数量,然后统计应该是0的位置上有几个1,这样就得到了我们需要的最少操作次数,对于每一次操作,如果当前还至少剩余i次操作需要进行,那么转移到i-1就是正好要选出i个1和i个0也就是i*i中可能,所以i转移到i-1的概率为p=i*i/c(n,2),进行无效操作也就是转移到i的概率是1-p,所以dp[i]=i*i/c(n,2)*dp[i-1]+1-i*i/c(n,2)*dp[i]+1,化简得dp[i]=dp[i-1]+1/p,所以我们最终要求的初始剩余cnt1次操作时的期望按照上述转移式展开化简就等于将每一步的1/p累加
- #include
- #include
- using namespace std;
- const int N = 2e5 + 5, MOD = 998244353;
- typedef long long ll;
- int a[N];
- ll qpow(ll a, ll b)
- {//快速幂求逆元
- ll ret = 1;
- while (b)
- {
- if (b & 1)
- {
- ret = ret * a % MOD;
- }
- a = a * a % MOD;
- b >>= 1;
- }
- return ret % MOD;
- }
- int main()
- {
- int t;
- cin >> t;
- while (t--)
- {
- ll n;
- scanf("%lld", &n);
- int cnt0 = 0;
- for (int i = 1; i <= n; i++)
- {
- scanf("%d", &a[i]);
- if (!a[i])
- {
- cnt0++;//统计0的个数
- }
- }
- int cnt1 = 0;
- for (int i = 1; i <= cnt0; i++)
- {
- if (a[i])
- {
- cnt1++;//统计应该是0的位置有多少1
- }
- }
- ll ans = 0;
- ll temp = n * (n - 1) / 2 % MOD;//c(n,2)
- for (ll i = 1; i <= cnt1; i++)
- {
- ans = (ans + qpow(i * i % MOD, MOD - 2) * temp % MOD) % MOD;//每一步概率之和
- }
- printf("%lld\n", ans);
- }
- return 0;
- }