1. 程式人生 > >[bzoj4872]分手是祝願

[bzoj4872]分手是祝願

type 包括 ide lld 模擬 mod 接下來 ret bsp

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 題解: 熟悉的題目啊...(17年省選打醬油的回憶)這個題的靈魂就是期望式子的推導 我們先來考慮不隨機的情況: 我們應該從大編號往小編號(因為大編號只能按自己才能改變)一路按過去 這樣模擬一遍我們就可以算出來正常情況下要按多少次,設這個次數為num 那麽,如果考慮等概率瞎按
的情況呢?
我們設f[i]為從"剩下i個位置亮按到到剩下i-1個位置亮"需要按的次數 那麽有2種情況 1°i/n的概率按到一個應該按的燈,直接成功 2°(n-i)/n的概率按到一個不應該按的燈,這時先要按回來到i,再按到i-1 所以f[i]=i/n+(n-i)/n*(b[i+1]+b[i]+1),再給它大力化簡一下,把f[i]放在一邊 得到:f[i]=(f[i+1]*(n-i)+n)/i 求出來之後,我們就要統計(k,num]間num的累加之和,再乘上階乘就完事了,ans=Σ{f[i],i∈(k,num]}*n! 如果n==k或者k>num,一上來就直接最優解,不用隨機,這時ans=num*n! 在計算的時候處理上取模(逆元之類的),就可以A掉這道題了 代碼見下: 技術分享
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL;
const int N=100010;
const int mod=100003; 
int n,k,a[N];
LL f[N],num;
LL quick_mod(LL di,int mi)
{
    LL ret=1;
    while(mi)
    {
        if(mi&1)ret=ret*di%mod;
        di=di*di%mod;
        mi>>=1;
    }
    return ret;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=n;i>=1;i--)
        if(a[i])
        {
            for(int j=1;j*j<=i;j++)
                if(!(i%j))
                {
                    a[j]^=1;
                    if(j*j!=i)a[i/j]^=1;
                }
            num++;
        }
    for(LL i=n;i>=1;i--)
        f[i]=(f[i+1]*(n-i)%mod+n)%mod*quick_mod(i,mod-2)%mod;
    LL t=0;
    if(num<k||n==k)t=num;
    else
    {
        for(LL i=num;i>k;i--)
            t=(t+f[i])%mod; 
        t=(t+k)%mod; 
    }
    for(LL i=1;i<=n;i++)
        t=t*i%mod;
    printf("%lld",t);
}
bzoj4872

[bzoj4872]分手是祝願