P3750 [六省联考 2017] 分手是祝愿

处理出来一共有个多少的要摁的开关 (最优的方法是摁多少次)

我们可以先从 \(k\) 入手,从后往前扫,只要遇到 \(1\) 的位置就操作,并更新编号为 \(i\) 的约数的点

  • 一个点不会被操作 \(2\) 次以上,因为 \(2\) 次操作相当于没操作

  • 操作 \(i\) 不会影响到比ii大的数

  • 所以从后往前扫,若遇到 \(1\) 不操作,那么前面的操作也不会改变这个 \(1\) ,所以必须操作

这时我们设需要摁的总开关数为 \(cnt\)

(借鉴自本篇博客

接下来我们该推随机处理得式子

\(f[i]\) 表示从 \(i\) 个需要按的开关到 \(i-1\) 个需要按的开关的期望操作次数,则我们可以得到以下式子

\[f[i] = \frac{i}{n} + \frac{n-i}{n} \times (f[i] + f[i + 1] + 1) \]

\(\frac{i}{n}\) 这里面的 \(i\) 表示剩下有 \(i\) 个正确的开关我们还没有摁,\(n\) 是我们总共有的开关个数。表示从 \(n\) 个开关里面选,选中正确的开关键位的概率.

\(\frac{n-1}{n}\) 表示按到错误的灯的概率,因为按错了,所以在之后的操作中需要将这个按键按回来,所以就多了一个需要按的键,要加 \(1\)。同时因为我们按错了,所以需要从 \(i+1\) 转移到 \(i\) ,再从 \(i\) 转移到 \(i-1\)。这就是为什么要加 \(f[i]+f[i+1]\)

对上述式子化简

\[f[i] = \frac{n + (n-i) \times f[i+1]}{i} \]

得到结果

在求出这个式子之后,我们先比较一下必须按的按键个数和 \(k\) ,如果还要小,就肯定是前者作为答案

不然直接把 \(f[cnt]+f[cnt-1]+.....+f[k+1]\) 作为答案就行,相当于把正确的开关个数从 \(cnt\) 个按到 \(k\) 个的期望值,最后再加上剩余要按得最有按键个数 \(k\) 就行。

提示

我们从 \(n\), 转移过来而不是 \(cnt\) 转移过来是为了处理 \(f[cnt+1]\)。在 \(f[n+1]\) 时我们知道值一定为 \(0\) (一共有 \(n\) 个开关,从 \(n+1\) 个开关到 \(n\) 个开关一定为 \(0\))但我们不知道 \(f[cnt+1]\) 的值,所以要从 \(f[n+1]\) 一个一个往前推。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <algorithm>
#define mod 100003

using namespace std;

long long n, k, a[5211314], cnt;
long long f[5211314], inv[5211314];

int main() {
	scanf("%lld%lld", &n, &k);
	inv[1] = 1;
	for (int i = 2; i <= n; ++ i) {
		inv[i] = ((mod - mod / i) * inv[mod % i]) % mod;
	}
	for (int i = 1; i <= n; ++ i) {
		scanf("%lld", &a[i]);
	}
	for (int i = n; i >= 1; -- i) {
		if (a[i] == 1) {
			cnt ++;
			for (int j = 1; j * j <= i; ++ j) {
				if (i % j == 0) {
					a[j] ^= 1;
					if (j * j != i) {
						a[i / j] ^= 1;
					}
				}
			}
		}
	}
//	cout << cnt << endl;
	f[n + 1] = 0;
	long long tem;
	for (int i = n; i >= 1; -- i) {
		tem = ((((n - i) * f[i + 1]) % mod) + n) % mod;
		tem = tem * inv[i] % mod;
		f[i] = tem;		
	}
	tem = 0;
	if (cnt <= k) tem = cnt;
	else {
		for (int i = cnt; i > k; -- i) {
			tem = (tem + f[i]) % mod;
		}
		tem = (tem + k) % mod;
	}
	for (long long i = 1; i <= n; ++ i) {
		tem = (tem * i) % mod;
	}
	printf("%lld\n", tem);
	return 0;
}
posted @ 2023-07-23 21:45  觉清风  阅读(7)  评论(0编辑  收藏  举报