题意
给定只有0和1的数组。
每次随机选择两个位置进行交换。
问,当交换多少次后,这个数组有序。求该交换次数的期望值。答案对998244353取模
思路
令g为数组中0的个数
令o为数组中前g个元素中0的个数。
当数组有序,则前g个元素都是0.
定义dp[k]表示前g个元素,有k个0时,使数组有序,需要交换的期望次数。
显然有dp[g]=0.
转移方程
p = 2*(g-k)*(g-k)/(n*(n-1))
dp[k] = 1 + dp[k]*(1-p) +dp[k+1]*p
涉及到逆运算,我们预处理,计算出1到200000的在取模意义下的倒数值
// 线性求逆元 inv[i] = 1/i (mod 998244353)
void init() {
inv[0] = 1;
inv[1] = 1;
for (int i = 2; i < maxn; ++i) {
inv[i] = mul(mod - mod / i, inv[mod%i]);
}
}
对于大于的200000的元素,我们可以用快速幂求其取模意义下的倒数值
inv = quickp(p, mod - 2); // 1/p = p^(mod-2) (mod)
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pcc pair<char, char>
#define pii pair<int, int>
#define inf 0x3f3f3f3f
const int maxn = 200010;
const int mod = 998244353;
int inv[maxn];
/*
* 0 <= a, b < mod
*/
int add_mod(int a, int b) {
int res = a + b;
if (res >= mod) res -= mod;
return res;
}
int mul(int a, int b) {
return 1LL * a * b % mod;
}
int quickp(int a,int p) {
int res = 1;
while(p) {
if(p & 1) res = mul(res, a);
a = mul(a, a);
p >>= 1;
}
return res;
}
// 线性求逆元 inv[i] = 1/i (mod 998244353)
void init() {
inv[0] = 1;
inv[1] = 1;
for (int i = 2; i < maxn; ++i) {
inv[i] = mul(mod - mod / i, inv[mod%i]);
}
}
int n, x;
int a[maxn];
void solve() {
scanf("%d", &n);
// g: number of zero.
// s: number of zero in first g positions.
int g = 0, o = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d", &x);
a[i] = x;
if (!x) ++g;
}
for (int i = 1; i <= g; ++i) {
if (!a[i]) ++o;
}
// dp[k] = 1 + dp[k]*(1-p) +dp[k+1]*p
int pre = 0; // dp[g]
int cur = 0;
for (int k = g - 1; k >= o; --k) {
// pre: dp[k+1]
// cur: dp[k]
// p = 2*(g-k)*(g-k)/(n*(n-1))
int p = mul(2, mul(g - k, g - k));
p = mul(p, inv[n]);
p = mul(p, inv[n-1]);
// dp[k] = (dp[k+1]*p + 1)/p, 1/p = p^(mod-2) (mod)
cur = add_mod(1, mul(pre, p));
cur = mul(cur, quickp(p, mod - 2));
pre = cur;
}
printf("%d\n", cur);
}
int main() {
int t = 1;
init();
scanf("%d", &t);
int cas = 1;
while (t--) {
// printf("cas %d:\n", cas++);
solve();
}
}
GZH
对方正在debug