BZOJ4872: [Shoi2017]分手是祝願-期望dp

傳送門

題意:

B 君在玩一個遊戲,這個遊戲由 n 個燈和 n 個開關組成,給定這 n 個燈的初始狀態,下標爲從 1 到 n 的正整數。

每個燈有兩個狀態亮和滅,我們用 1 來表示這個燈是亮的,用 0 表示這個燈是滅的,遊戲的目標是使所有燈都滅掉。

但是當操作第 i 個開關時,所有編號爲 i 的約數(包括 1 和 i)的燈的狀態都會被改變,即從亮變成滅,或者是從滅變成亮。

B 君發現這個遊戲很難,於是想到了這樣的一個策略,每次等概率隨機操作一個開關,直到所有燈都滅掉。

這個策略需要的操作次數很多, B 君想到這樣的一個優化。如果當前局面,可以通過操作小於等於 k 個開關使所有燈都滅掉,那麼他將不再隨機,直接選擇操作次數最小的操作方法(這個策略顯然小於等於 k 步)操作這些開關。

B 君想知道按照這個策略(也就是先隨機操作,最後小於等於 k 步,使用操作次數最小的操作方法)的操作次數的期望。

這個期望可能很大,但是 B 君發現這個期望乘以 n 的階乘一定是整數,所以他只需要知道這個整數對 100003 取模之後的結果。

1 ≤ n ≤ 100000; 0 ≤ k ≤ n

Solution:

日常被期望題艹爆…

首先考慮當前狀態達到遊戲目標的最小步數:對於一個數i,小於他的位置開關一定不會對它產生影響,所以可能影響到它的只有它前面和它本身,那麼我們可以知道從後往前關燈一定是最優的,暴力搞就行

需要篩出每個數的因數,複雜度O(nlogn)

下面我們再來看期望:f[i]表示從達到遊戲目標的最小步數爲i變爲達到遊戲目標的最小步數爲i-1的期望操作次數

因爲對於每個不同狀態到達遊戲目標所需操作的開關都是唯一的,那麼就有轉移:f[i]=in+(1in)(1+f[i+1]+f[i])

(n個開關中有i個是可以減少步數的開關,剩餘的n-i個會增加一步)

移項得:f[i]=n+(ni)f[i+1]i

再考慮我們的初始狀態:當所有位置都爲正確選擇時,選任何一個位置都不會增加錯誤選擇,所以f[n]=1

最後把f[k+1]~f[到達遊戲目標的最小步數]累加起來即可

代碼:

#include<cstdio>
#include<vector>
#include<iostream>
using namespace std;
const int mod=100003;
vector<int>d[100010];
int n,m,stp;
int a[100010];
int f[100010];
int fast_pow(int x,int a)
{
    int ans=1;
    for (;a;a>>=1,x=1ll*x*x%mod)
        if (a&1) ans=1ll*ans*x%mod;
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for (int i=1;i<=n;i++)
        for (int j=i;j<=n;j+=i) d[j].push_back(i);
    for (int i=n;i>=1;i--)
        if (a[i])
        {
            stp++;
            for (int j=0;j<d[i].size();j++)
                a[d[i][j]]^=1;
        }
    int ans=0;
    if (stp<=m) ans=stp;
    else
    {
        f[n]=1;
        for (int i=n-1;i>m;i--) f[i]=(1ll*n+1ll*(n-i)*f[i+1])%mod*fast_pow(i,mod-2)%mod;//,cout<<f[i]<<" ";
        ans=m;
        for (int i=m+1;i<=stp;i++) {ans+=f[i];if (ans>=mod) ans-=mod;}
    }
    for (int i=1;i<=n;i++) ans=1ll*ans*i%mod;
    printf("%d",ans); 
}
發佈了115 篇原創文章 · 獲贊 15 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章