[SHOI2017]分手是祝願

鏈接:https://ac.nowcoder.com/acm/problem/20437
來源:牛客網
題目:

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 個燈的初始情況。
1 ≤ n ≤ 100000, 0 ≤ k ≤ n;

輸出描述:

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

輸入:

4 0
0 0 1 1

輸出:

512

題解:

首先,我們應當明確三件事情:

1.如果直接考慮用最少的操作關掉所有燈,那麼從最後面的燈開始關,如果燈亮着,關掉它,並翻轉它所有因數編號的燈。一直遍歷到第一盞燈爲止。

2.假設關掉所有燈共需cnt次操作,那麼任意交換這cnt次操作中的兩次,不影響最終結果。

3.最少操作是唯一確定的。

假設f[i]爲在最少操作次數爲i的情況下,需要操作的平均次數。

由題意知,i<=k時,f[i]=i。

當i>k時,f[i]=(i/n)*f[i-1]+((n-i)/n)*f[i+1]+1(*)

(i/n)*f[i-1]表示如果選中了n次操作中的其中i次,由條件1知其最少操作次數-1;

((n-i)/n)*f[i+1]表示如果選中了剩下的n-i次操作,那麼你就得再花一次操作來抵消此次操作,因此最少操作次數 +1。

令d[i]=f[i]-f[i-1],(*)式可化爲d[i]=n/i+(n-i)/i*d[i+1]。

易知f[n]=f[n-1]+1,因此d[n]=1,可以通過遞推方式將d[i]計算完畢,進而算出i>k時的所有f[i]。

由條件1計算得最少操作爲cnt,那麼最終答案爲n!*f[cnt]%100003。

另:除法取模需藉助逆元計算。

代碼:

#include <bits/stdc++.h>
#define maxn 100010
#define ll long long
using namespace std;
const ll mod=100003;
ll n,k,f[maxn],s[maxn],d[maxn],opt[maxn];


ll ni[maxn],cnt=0;
void init(){
	ll i,j;
	memset(f,0,sizeof(f));
	memset(d,0,sizeof(d));
	memset(ni,0,sizeof(ni));
	ni[1]=1;
	for(i=2;i<mod;i++)ni[i]=-ni[mod%i]*(mod/i)%mod+mod;
	
	for(i=n;i>=1;i--){
		if(s[i]==1){
			cnt++;
			for(j=1;j<=sqrt(i+0.5);j++){
				if(i%j==0&&i>j){
					ll c=i/j;
					if(c>j){
						s[j]=s[j]^1;
						s[c]=s[c]^1;
					}
					else s[j]=s[j]^1;
				}
			}
		}
	}
}


int main(){
	int i;
	scanf("%lld%lld",&n,&k);
	for(i=1;i<=n;i++)scanf("%lld",&s[i]);
	init();
	d[n]=1;
	for(i=n-1;i>=1;i--){
		d[i]=((n-i)*ni[i]+mod)%mod*d[i+1]%mod+(n*ni[i]+mod)%mod;
		d[i]=d[i]%mod;
	}
	for(i=1;i<=k;i++)f[i]=i;
	for(i=k+1;i<=n;i++){
		f[i]=f[i-1]+d[i]+mod;
		f[i]=f[i]%mod;	
	}
	ll ans=1;
	for(i=1;i<=n;i++){
		ans*=i;
		ans+=mod;
		ans%=mod;
	}
	cout<<ans*f[cnt]%mod;
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章