BZOJ 4872 [SHOI2017]分手是祝願

題目在這裏呀~

這題讓我理解了很長一段時間(兩天誒),當然不是全天啦。

題意

有n盞燈初始有一個狀態,現隨機選一盞燈i,將i和它的約數的燈的狀態都改變。
噹噹前狀態採用最優方案還需要的步數小於等於k時,直接採用最優方案。問期望步數。

題解

先考慮用最優方案需要幾次,即從大到小枚舉,然後如果要改就改。(應該可以省略吧qaq
然後考慮概率DP,f[i]表示將有i盞燈亮着變爲i-1盞的期望。
有 n/i 的概率選到亮着的燈,則只需要1次。有1-n/i的概率選到沒亮的燈,那麼在之後的某一次操作中一定還會將燈在關掉,所以期望步數爲(1+f[i+1]+f[i])。

所以

f[i]=ni+nii(1+f[i+1]+f[i])

邊界情況f[n]=1,f[i]=1(i=1~k)

//Suplex
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
#define N 100000+1000
using namespace std;
const int mod=100003;
int n,k,a[N],flag[N],s;
ll fac,ans,inv[N],f[N];

void pre()
{
    fac=1;
    for(int i=1;i<=n;i++) fac=fac*(ll)i % mod;
    inv[1]=1;
    for(int i=2;i<=n;i++) inv[i]=(mod-mod/i)*inv[mod % i] % mod;
}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    pre();
    for(int i=n;i;i--){
        int now=a[i];
        for(int j=i+i;j<=n;j+=i) if(flag[j]) now^=1;
        if(now) flag[i]=1,s++;
    }
    if(s<=k){printf("%lld\n",(ll)s*fac % mod);return 0;}
    f[n]=1;
    for(int i=1;i<=k;i++) f[i]=1;
    for(int i=n-1;i>k;i--){
        f[i]=(ll)(n-i)*(f[i+1]+1) % mod*inv[i] % mod+1;
        f[i] %= mod;
    }
    for(int i=1;i<=s;i++) ans+=f[i],ans %= mod;
    ans=ans * fac % mod;
    printf("%lld\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章