首页 > 技术文章 > [六省联考2017]分手是祝愿——期望DP

dummyummy 2019-06-17 10:00 原文

原题戳这里
首先可以确定的是最优策略一定是从大到小开始,遇到亮的就关掉,因此我们可以\(O(nlogn)\)的预处理出初始局面需要的最小操作次数\(tot\)
然后容(hen)易(nan)发现即使加上了随机,那\(tot\)个也一定要被操作,也就是说操作这\(tot\)个之外的都是没用的。
于是就可以\(dp\)了,设\(f[i]\)表示还剩\(i\)个必须要操作的未操作,转移如下:
\(f[i]=\frac{i}{n}+\frac{n-i}{n}(f[i]+f[i+1]+1)\)
移项得到\(f[i]=\frac{(n-i)f[i+1]+n}{i}\)
边界\(f[n]=1\)
最后如果\(tot\leqslant k\)直接输出\(tot*n!\),否则输出\(k+n!\sum\limits_{i=k+1}^{tot}f[i]\)

#include <bits/stdc++.h>

using namespace std;

#define MAXN 100000
#define MOD 100003

int n, k, tot, ans, a[MAXN + 5], f[MAXN + 5], fac = 1;
vector<int> G[MAXN + 5];

int fpow(int x, int p) {
	int ret = 1;
	while(p) {
		if(p & 1) ret = 1LL * ret * x % MOD;
		x = 1LL * x * x % MOD;
		p >>= 1;
	}
	return ret;
}

int main() {
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
		for (int j = i; j <= n; j += i) G[j].push_back(i);
		fac = 1LL * fac * i % MOD;
	}
	for (int i = n; i >= 1; --i) {
		if(!a[i]) continue;
		tot++;
		for (auto j : G[i]) a[j] ^= 1;
	}
	if (tot <= k) ans = 1LL * tot * fac % MOD;
	else {
		f[n] = 1, ans = k;
		for (int i = n - 1; i >= 1; --i) f[i] = (1LL * (n - i) * f[i + 1] % MOD + n) * fpow(i, MOD-2) % MOD;
		for (int i = k + 1; i <= tot; ++i) ans = (ans + f[i]) % MOD;
		ans = 1LL * ans * fac % MOD;
	}
	printf("%d\n", ans);
	return 0;
}

推荐阅读