Loading

【题解】P3750 [六省联考 2017] 分手是祝愿

出题老哥收收味吧,阿米奈!

记一下几个常用的手段。

昨晚 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;
}
posted @ 2023-02-02 20:08  kymru  阅读(20)  评论(0编辑  收藏  举报