Zeit und Raum trennen dich und mich.c++
時空將你我分開。B 君在玩一個遊戲,這個遊戲由 n 個燈和 n 個開關組成,給定這 n 個燈的初始狀態,下標爲從 1 到 n 的正整數。每一個燈有兩個狀態亮和滅,咱們用 1 來表示這個燈是亮的,用 0 表示這個燈是滅的,遊戲的目標是使全部燈都滅掉。可是當操做第 i 個開關時,全部編號爲 i 的約數(包括 1 和 i)的燈的狀態都會被改變,即從亮變成滅,或者是從滅變成亮。B 君發現這個遊戲很難,因而想到了這樣的一個策略,每次等機率隨機操做一個開關,直到全部燈都滅掉。這個策略須要的操做次數不少, B 君想到這樣的一個優化。若是當前局面,能夠經過操做小於等於 k 個開關使全部燈都滅掉,那麼他將再也不隨機,直接選擇操做次數最小的操做方法(這個策略顯然小於等於 k 步)操做這些開關。B 君想知道按照這個策略(也就是先隨機操做,最後小於等於 k 步,使用操做次數最小的操做方法)的操做次數的指望。這個指望可能很大,可是 B 君發現這個指望乘以 n 的階乘必定是整數,因此他只須要知道這個整數對 100003 取模以後的結果。算法
第一行兩個整數 n, k。優化
接下來一行 n 個整數,每一個整數是 0 或者 1,其中第 i 個整數表示第 i 個燈的初始狀況。spa
1 ≤ n ≤ 100000, 0 ≤ k ≤ n;code
輸出一行,爲操做次數的指望乘以 n 的階乘對 100003 取模以後的結果。遊戲
4 0
0 0 1 1ip
512input
假設沒有隨機it
咱們考慮當前的最優算法,確定是從後往前來看有哪些須要被更新的io
那麼這樣就很容易算出最開始咱們最少須要走幾步
而後是\(dp_{i}\)表示從最少i步走到最少i-1步的指望步數
咱們考慮由於當前有i步都是須要走的,因此當前這一步轉移到i-1的機率是\(\frac{i}{n}\)
而後就是走到i+1,機率\(1-\frac{i}{n}\),指望步數能夠從\(dp_{i+1}\)轉移
總的轉移是\(dp_{i}=\frac{i}{n}+(1-\frac{i}{n})*(1+dp_{i}+dp_{i+1})\)
移項消元獲得\(dp_{i}=\frac{n+(n-i)*dp_{i+1}}{i}\)
而後就作完了,前綴和累加就好了
#include<bits/stdc++.h> using namespace std; const int Mod = 1e5 + 3; const int N = 1e5 + 10; int n, k, a[N], f[N], fac = 1; int add(int a, int b) { return (a += b) >= Mod ? a - Mod : a; } int sub(int a, int b) { return (a -= b) < 0 ? a + Mod : a; } int mul(int a, int b) { return 1ll * a * b % Mod; } int fast_pow(int a, int b) { int res = 1; for (; b; b >>= 1, a = mul(a, a)) { if (b & 1) res = mul(res, a); } return res; } int main() { scanf("%d %d", &n, &k); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); fac = mul(fac, i); } int num = 0; for (int i = n; i >= 1; i--) { for (int j = i << 1; j <= n; j += i) { a[i] ^= a[j]; } if (a[i]) ++num; } for (int i = 1; i <= k; i++) f[i] = 1; for (int i = n; i > k; i--) { f[i] = mul(add(n, mul(n - i, f[i + 1])), fast_pow(i, Mod - 2)); } int ans = 0; for (int i = 1; i <= num; i++) { ans = add(ans, f[i]); } printf("%d", mul(ans, fac)); return 0; }