E. Wish I Knew How to Sort(数论/概率dp)

题目
参考

题意

给定只有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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值