出题老哥收收味吧,阿米奈!
记一下几个常用的手段。
昨晚 CF 的 D 是差不多的思路吧,不然不会来做。
思路
期望 dp.
先做一些准备工作,求一下逆元和每个数的因数,复杂度 \(O(n \ln n)\).
考虑操作每个数得到的贡献。
-
取反,意味着每个数最多被操作一次,转化成考虑选择若干数操作。
-
操作因数,意味着每个数只能对小于等于它的数产生贡献,并且每个数的贡献是互异的。
-
期望 dp,考虑倒推。
考虑到产生 \(k\) 次内的最优策略时,剩余操作的期望次数是 \(k\). 所以考虑构造最优策略,不然这个条件很难判定。
结合 2,降序考虑每个数的贡献对答案的正确性没有影响。从大到小考虑,如果当前数的位置是 \(1\),说明必须操作当前数。于是可以得到最优策略。
对于初始局面模拟一次,可以发现想要得到全 \(0\) 的局面,最终被操作的数是确定的,并且操作这些数的先后顺序不影响答案。
于是可以考虑设 \(f[i]\) 表示还有 \(i\) 个数需要操作时的期望步数。
这样转移有后效性,这里有一步比较巧妙:令 \(f[i]\) 表示从还有 \(i\) 个数需要操作转移到还有 \(i - 1\) 个数需要操作的期望步数。
那么操作到正确位置的概率是 \(frac{i}{n}\),代价是 \(1\);操作到错误位置的概率是 \(\frac{n - i}{n}\),代价是 \(f[i + 1] + f[i] + 1\).
所以 \(f[i] = \frac{n}{i} + \frac{n - i}{n} (f[i + 1] + f[i] + 1)\),整理得:
\(f[i] = \frac{n + (n - i) f[i + 1]}{i}\).
注意要分讨初始局面是否满足随机条件。
时间复杂度 \(O(n \ln n)\).
代码
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 1e5 + 5;
const int mod = 100003;
int n, k;
int a[maxn], f[maxn], inv[maxn];
vector<int> fac[maxn];
int main()
{
scanf("%d%d", &n, &k);
inv[1] = 1; for (int i = 2; i <= n; i++) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j += i)
fac[j].push_back(i);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
int cnt = 0;
for (int i = n; i >= 1; i--)
if (a[i])
{
cnt++;
for (int v : fac[i]) a[v] ^= 1;
}
for (int i = n; i >= 1; i--) f[i] = (n + 1ll * (n - i) * f[i + 1] % mod) % mod * inv[i] % mod;
int ans = 0;
if (cnt <= k) ans = cnt;
else
{
for (int i = cnt; i >= k + 1; i--) ans = (ans + f[i]) % mod;
ans = (ans + k) % mod;
}
for (int i = 1; i <= n; i++) ans = 1ll * ans * i % mod;
printf("%lld\n", ans);
return 0;
}
标签:六省,int,题解,frac,maxn,ans,操作,联考,mod
From: https://www.cnblogs.com/lingspace/p/p3750.html