2017 [六省聯考] T5 分手是祝願

4872: [Shoi2017]分手是祝願

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 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

Sample Output

512

HINT

 

Source

 
首先一個狀態最少須要多少步是能夠 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;
}
相關文章
相關標籤/搜索