【動態規劃01】bzoj4872分手是祝願(期望)

NOI將近,本蒟蒻學校名額水進了省隊之後在家自學,然而年輕的我並沒有意識到在家自學的學習效率近乎爲0。
爲了提高自己不FE的概率,所以在博客上每日更新一道dp題,達到督促自己的目的。
再加上本人是一個究極蒟蒻,所以題解自然會寫的簡單明瞭,連⑨都能明白(?)。

扯淡有點久,更新的第一道dp題就決定是省選考試的這道比較容易的期望Dp題目。

題目描述

Zeit und Raum trennen dich und mich.

時空將你我分開。

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

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

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

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

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

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

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

輸入輸出格式

輸入格式:

第一行兩個整數 n, k。

接下來一行 n 個整數,每個整數是 0 或者 1,其中第 i 個整數表示第 i 個燈的初始情況。

輸出格式:

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

這道題的數據範圍是 對於 100% 的測試點, 1 ≤ n ≤ 100000; 0 ≤ k ≤ n。
首先我們思考這樣一個問題,假如可以通過k次(或以下)操作將其全變爲0,那麼這個操作次數是一定的,首先同一個燈不可能進行兩次操作(1^1^1==1),那麼對於一組燈我們暴力從後往前進行枚舉,如果這個燈是1那麼必然要將其滅掉(選擇更小的數並不能對其造成影響),我們可以在O(sqrt(n)*n)的時間複雜度內暴力得出次數值。

這道題的數據十分的水,如果你按照以上所寫的暴力直接輸出期望(概率100%*次數)可以得到80分,當然如果你和我一樣將所有的燈變爲1的話,你就會爆0(論D2得分如何從三位數變成個位數)。

那麼對於需要k次以上操作才能到達的情況,很明顯如果表示每次燈的狀態這是不可能做到的。但事實上我們會發現對於每個狀態,我們只需要記錄他到達全滅狀態的次數就可以了。
顯然經過每次操作到達的下一個狀態一定是i+1或者i-1。
設f[i]爲狀態爲i時的期望,那麼對於i<=k時,有f[i]=i。
對於i>k,f[i]=f[i-1]in +f[i+1]nin +1(1==(i/n)+((n-i)/n)
/*
由期望公式
E=Σp*(E1+X1)+Σp2*(E+X2)
其中E1爲下一個狀態,p1和X1分別爲將當前狀態轉移到下一個狀態的概率和花費,p2和X2分別爲保持當前狀態的概率和花費。
以上轉自http://blog.csdn.net/qq_31759205/article/details/54730101
*/
已知邊界條件f[n]=f[n-1]+1,因爲如果需要最大步數的時候,無論取任何操作,一定都會使操作數減少。

然而這個遞推式成立的條件是f[i-1]存在,那麼當k=1/k=0的時候顯然無法得出結論。
那麼這時候要怎麼辦?那麼就是將遞推式中存在的f[i-1]給消掉。

設g[i]=f[i]-f[i-1],則有g[i+1]=f[i+1]-f[i]。
將f[i+1]用f[i]與f[i-1]表示
g[i+1]=f[i+1]-f[i]=(f[i]-1-f[i-1]in )nni -f[i]
=(f[i]f[i1])inni
=g[i]inni
由此可以得出g[i]=g[i+1](ni)+ni
同時g[n]=f[n]-f[n-1]=1,故遞推可求解。
故答案爲f[cnt],而此時,由於f[k]=k已知,故f[n]可以表示爲cntk+1g[i] +k
綜上爲正解。

#include<bits/stdc++.h>
#define fer(i,j,n) for(int i=j;i<=n;i++)
#define far(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
const int maxn=100010;
const int INF=1e9+7;
const int mod=100003;
using namespace std;
/*----------------------------------------------------------------------------*/
inline ll read()
{
    char ls;ll x=0,sng=1;
    for(;ls<'0'||ls>'9';ls=getchar())if(ls=='-')sng=-1;
    for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';
    return x*sng;
}
/*----------------------------------------------------------------------------*/
ll n,k,light[maxn],g[maxn];
ll cnt=0,ans=0;
void change(int x)
{
    fer(i,1,sqrt(x))
        if(x%i==0)
        {
            light[i]^=1;
            if(i!=sqrt(x))light[x/i]^=1;
        }
    return ;
}
ll pw(ll x)
{
    ll y=mod-2,ans=1;
    for(;y;y>>=1,x=x*x%mod)
        if(y&1)ans=ans*x%mod;
    return ans;
}
int main()
{
    n=read();k=read();
    fer(i,1,n)light[i]=read();
    far(i,n,1)
        if(light[i]==1)
        {
            change(i);
            cnt++;
        }
    if(cnt<=k)
    {
        for(ll i=2;i<=n;i++)
        cnt=cnt*i%mod;
        cout<<cnt<<endl;
        return 0;
    }
    g[n]=1;
    far(i,n-1,k+1)g[i]=(g[i+1]*(n-i)+n)*pw(i)%mod;//逆元
    ans=k;
    fer(i,k+1,cnt)ans+=g[i];
    for(ll i=2;i<=n;i++)
        ans=ans*i%mod;
    cout<<ans<<endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章