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

Description

洛谷传送门

Solution

这道题 \(CSP\) 前就计划着做来着,结果一直咕咕咕了,现在来补一下吧。

当时点开这道题主要是被题目吸引进来的,结果发现出题人是标题党,差评!

好吧,下面我们在来分析一下这道题。

我们首先考虑最优情况,其实这个是比较好想的。

不难发现,点击编号较小的开关对于编号较大的开关是没有影响的,所以我们从大到小枚举每一个灯,如果这个灯开着,就关上,并把它的约数全部操作一遍。

这样一来,就是最少的操作次数了。

再来看随机次数怎么计算,这个就要动态规划了,这个状态定义的非常巧妙。

我们设 \(f_i\) 表示从需要按 \(i\) 个开关到需要按 \(i - 1\) 个开关期望操作次数。

那么有转移方程:

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

\(\frac{i}{n}\) 的概率按到需要按的开关,贡献为 1,有 \(\frac{n - i}{n}\) 的概率按到错误的键,这时我们想要转移到 \(f_{i - 1}\) 的话,就必须先从 \(f_{i + 1}\) 转移到 \(f_i\),再从 \(f_i\) 转移到 \(f_{i - 1}\),所以要加上 \(f_i\)\(f_{i + 1}\)

再把这个式子化简一下(开括号再移项),得:

\[f_i = \frac{(n - i)f_{i + 1} + n}{i} \]

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define ll long long

using namespace std;

const ll mod = 100003;
const ll N = 1e5 + 10;
ll n, k, cnt;
ll a[N], f[N];

inline ll qpow(ll a, ll b){
    ll res = 1;
    while(b){
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

signed main(){
    scanf("%lld%lld", &n, &k);
    for(ll i = 1; i <= n; ++i)
        scanf("%lld", &a[i]);
    for(ll i = n; i >= 1; --i){
        if(a[i]){
            cnt++;
            for(ll j = 1; j * j <= i; ++j)
                if(i % j == 0){
                    a[j] ^= 1;
                    if(j * j != i) a[i / j] ^= 1;
                }
        }
    }
    for(ll i = n; i >= 1; --i)
        f[i] = ((n - i) * f[i + 1] % mod + n) % mod * qpow(i, mod - 2) % mod;
    ll ans = 0;
    if(cnt <= k) ans = cnt;
    else{
        ans = k;
        for(ll i = cnt; i > k; --i) ans = (ans + f[i]) % mod;
    }
    for(ll i = 1; i <= n; ++i)
        ans = ans * i % mod;
    printf("%lld\n", ans);
    return 0;
}

End

posted @ 2021-10-25 20:00  xixike  阅读(48)  评论(0编辑  收藏  举报