4872: [Shoi2017]分手是祝願
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 458 Solved: 299
[Submit][Status][Discuss]
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
0 0 1 1
Sample Output
512
HINT
Source
黑吉遼滬冀晉六省聯考php
首先一個狀態最少須要多少步是能夠 O(N log N)算出來的,調和級數一下就行了。
並且咱們能夠發現,對於每個狀態,用最小步數關掉全部燈的方案是惟一的,考慮從後向前掃描,是1就動不然不動,老是最優的。
而後咱們就設 f[i] 爲關掉全部燈的最小步數爲i的指望答案。
顯然 當i<=k的時候,f[i]=i;其餘狀況,f[i] = (i/n) * f[i-1] + ((n-i)/n) * f[i+1] 。
爲何隨機的式子是長那個樣子的呢?
前面已經證實了對於每個狀態,最小步數關掉全部燈的方案是惟一的。
而又由於按燈的順序不影響答案,因此有(i/n)的概率最短步數減小;
但若是沒有按最短路上的燈,首先最短步數不會減小,由於最短路惟一;
並且也不會不變,由於若是還按原來的套路按燈的話最後不會都滅;
那麼按了不是最短方案的燈,會讓最短步數增長多少呢?
只能是1,由於能夠再按一步回去。
咱們能夠發現的是,f[n] = f[n-1] + 1。
經過這個向前推,能夠推出形如f[i] = f[i-1] + h[i]的式子,而且h[i] = (n / i) (1 + h[i+1] * (n-i) / n) 。
而後就能夠直接從前日後遞推了,對於i<=k,f[i] = i;不然, f[i] = f[i-1] + h[i] 。
而後就能夠A了2333
#include<bits/stdc++.h> #define ll long long #define maxn 100005 #define ha 100003 using namespace std; int n,k,T,a[maxn],opt[maxn]; int f[maxn],jc=1,inv[maxn],h[maxn]; inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x; } inline void init(){ inv[1]=1; for(int i=2;i<ha;i++) inv[i]=-inv[ha%i]*(ll)(ha/i)%ha+ha; } int main(){ init(); scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",a+i),jc=jc*(ll)i%ha; for(int i=n;i;i--){ for(int j=i<<1;j<=n;j+=i) a[i]^=opt[j]; if(a[i]) opt[i]=1,T++; } f[0]=0; for(int i=1;i<=k;i++) f[i]=i; h[n]=1; for(int i=n-1;i;i--) h[i]=add(n*(ll)inv[i]%ha,h[i+1]*(ll)(n-i)%ha*(ll)inv[i]%ha); for(int i=k+1;i<=T;i++) f[i]=add(f[i-1],h[i]); printf("%d\n",f[T]*(ll)jc%ha); return 0; }