[SHOI2017] 分手是祝願

Description

Zeit und Raum trennen dich und mich.
時空將你我分開。B 君在玩一個遊戲,這個遊戲由 n 個燈和 n 個開關組成,給定這 n 個燈的初始狀態,下標爲
從 1 到 n 的正整數。每個燈有兩個狀態亮和滅,我們用 1 來表示這個燈是亮的,用 0 表示這個燈是滅的,遊戲
的目標是使所有燈都滅掉。但是當操作第 i 個開關時,所有編號爲 i 的約數(包括 1 和 i)的燈的狀態都會被
改變,即從亮變成滅,或者是從滅變成亮。B 君發現這個遊戲很難,於是想到了這樣的一個策略,每次等概率隨機
操作一個開關,直到所有燈都滅掉。這個策略需要的操作次數很多, B 君想到這樣的一個優化。如果當前局面,
可以通過操作小於等於 k 個開關使所有燈都滅掉,那麼他將不再隨機,直接選擇操作次數最小的操作方法(這個
策略顯然小於等於 k 步)操作這些開關。B 君想知道按照這個策略(也就是先隨機操作,最後小於等於 k 步,使
用操作次數最小的操作方法)的操作次數的期望。這個期望可能很大,但是 B 君發現這個期望乘以 n 的階乘一定
是整數,所以他只需要知道這個整數對 100003 取模之後的結果。

Input

第一行兩個整數 n, k。
接下來一行 n 個整數,每個整數是 0 或者 1,其中第 i 個整數表示第 i 個燈的初始情況。
1 ≤ n ≤ 100000, 0 ≤ k ≤ n;

Output

輸出一行,爲操作次數的期望乘以 n 的階乘對 100003 取模之後的結果。

Sample Input

4 0
0 0 1 1

Sample Output

512

Solution

看完題面就覺得這題非常的喪。
先考慮不隨機按按鈕的情況。
我們發現在最優情況下,我們按編號從大到小的順序關燈,這樣的步數是最小的,因爲編號小的燈的按鈕開關不會影響編號大的燈的開關,這樣最壞情況下我們只要按 n 次就可以關閉所有燈。
然後我們發現, 50% 的數據 k=n ,這不是送分嗎,先打了再說。
就按編號從大到小 for 一遍, 如果這盞燈是開着的把所有編號爲它的因數的燈都按一遍,最後統計就可以了。
如果我們求出的最優解 Step 大於 k 該怎麼辦呢。
我們設 f[i] 爲我們需要按 i 次按鈕時能結束的期望步數。
易得我們有 in 的概率按到我們需要的按鈕 nin 的概率按到我們不需要的按鈕。
於是 f[i]=in×f[i1]+nin×f[i+1]+1 .
然而我們發現,我們最多有 n1 個方程而要求解 n 個變量,這顯然是不可行的。
所以我們把 f 數組差分一下, 設 g[i]=f[i]f[i1]
整理得 g[i]=n+(ni)×f[i+1]i
最後答案爲 k+i=k+1Stepg[i]

Code

#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <queue>
#include <set>
#include <stack>

#define R register
#define ll long long
#define db double
#define sqr(_x) (_x) * (_x)
#define Cmax(_a, _b) ((_a) < (_b) ? (_a) = (_b), 1 : 0)
#define Cmin(_a, _b) ((_a) > (_b) ? (_a) = (_b), 1 : 0)
#define Max(_a, _b) ((_a) > (_b) ? (_a) : (_b))
#define Min(_a, _b) ((_a) < (_b) ? (_a) : (_b))
#define Abs(_x) (_x < 0 ? (-(_x)) : (_x))

using namespace std;

namespace Dntcry
{
    inline int read()
    {
        R int a = 0, b = 1; R char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) (c == '-') ? b = -1 : 0;
        for(; c >= '0' && c <= '9'; c = getchar()) a = (a << 1) + (a << 3) + c - '0';
        return a * b;
    }
    inline ll lread()
    {
        R ll a = 0, b = 1; R char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) (c == '-') ? b = -1 : 0;
        for(; c >= '0' && c <= '9'; c = getchar()) a = (a << 1) + (a << 3) + c - '0';
        return a * b;
    }
    const int Maxn = 100010, Mod = 100003;
    int n, k;
    int v[Maxn];
    ll f[Maxn], test, Ans, fac, inv[Maxn];
    int Main()
    {
        n = read(), k = read();
        fac = inv[0] = inv[1] = 1; 
        for(R int i = 1; i <= n; i++) v[i] = read(), fac = 1ll * fac * i % Mod;
        for(R int i = n; i > 0; i--) if(v[i]) 
        {
            R int lim = (int)sqrt(i);
            ++test;
            for(R int j = 1; j <= lim; j++) 
                if(i % j == 0) 
                {
                    v[j] ^= 1;
                    if(j * j != i) v[i / j] ^= 1;
                }
        }
        if(test <= k) return !printf("%lld\n", test * fac % Mod);
        for(R int i = 2; i <= n; i++) 
            inv[i] = (Mod - Mod / i) * inv[Mod % i] % Mod;
        f[n] = 1;
        for(R int i = n - 1; i >= k; i--) 
            f[i] = 1ll * (n + 1ll * (n - i) * f[i + 1] % Mod) * inv[i] % Mod;
        for(R int i = test; i > k; i--) Ans = (Ans + f[i]) % Mod;
        Ans += k;
        printf("%lld\n", Ans * fac % Mod);
        return 0;
    }
}
int main()
{
    return Dntcry :: Main();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章