鏈接: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;
}