1. 程式人生 > >bzoj 4872: [Shoi2017]分手是祝願

bzoj 4872: [Shoi2017]分手是祝願

cst content mat csharp most 式表 期望 每次 ren

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

黑吉遼滬冀晉六省聯考

首先k=n的部分分;

考慮到如果要把第n號燈熄滅,那麽一定要關第n號燈,同理我們可以從後往前地選擇,然後每次sqrt(n)地修改一下每盞燈的狀態;

由於每個開關按了兩次等於沒有按,所以每個開關最多是會按一次,而且通過從後往前的貪心策略,最優的方案是唯一的,但是無關順序;

那麽我們設f[i]為還需要按i步的期望步數,由於最優的方案是確定的,所以我們需要判斷這一次隨機的開關是否在既定的i步之中;

如果是既定的i步之中的開關,那麽步數-1,如果按的是方案之外的開關,因為我們的最優策略是唯一的,所以我們需要再按一次來撤回這一次失誤,所以轉移大致是這樣:
技術分享

這個貌似不能直接遞推,

我們考慮將f[]數組差分,設g[i]=f[i]-f[i-1];

那麽我們得出g數組的遞推式:

技術分享

推到過程就是把f[i]用f的遞推式表示,然後在把差值用g[i+1]表示之類的;

那麽我們的答案為f[p]*n!,其中p為最小的操作步數;

//MADE BY QT666
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define int long long
using namespace std;
typedef long long ll;
const int N=100050;
const int mod=100003;
int a[N],n,k,p;
ll g[N];
ll qpow(ll x,ll y){
    ll ret=1;
    while(y){
	if(y&1) (ret*=x)%=mod;
	(x*=x)%=mod;y>>=1;
    }
    return ret;
}
main(){
    scanf("%lld%lld",&n,&k);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=n;i;i--){
	if(a[i]){
	    p++;
	    for(int j=1;j<=sqrt(i);j++){
		if(i%j==0){
		    if(j*j==i) a[j]^=1;
		    else a[j]^=1,a[i/j]^=1;
		}
	    }
	}
    }
    if(p<=k) {
	int ans=p;
	for(int i=1;i<=n;i++) (ans*=i)%=mod;
	cout<<ans<<endl;return 0;
    }
    g[n+1]=0;
    for(int i=n;i;i--) g[i]=(g[i+1]*(n-i)+n)*qpow(i,mod-2)%mod;
    int ans=k;
    for(int i=k+1;i<=p;i++) ans+=g[i];
    for(int i=1;i<=n;i++) (ans*=i)%=mod;
    printf("%lld\n",ans);
    return 0;
}

bzoj 4872: [Shoi2017]分手是祝願