[BZOJ 4872][SHOI&SXOI2017]分手是祝愿(期望Dp)

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 取模之后的结果。

Solution

最优解:从大到小遇到开着的灯就进行操作

出题人良心,考试的时候骗到70…(本来可以是80?)

期望只和操作次数有关,当前最少需要i步操作时,通过一次操作只可能使需要的操作加一或减一

设f[i]为还差i步的期望,如果i<=k,那么f[i]=i,否则f[i]=(f[i-1]*i+f[i+1]*(n-i))/n+1

设g[i]=f[i]-f[i-1],如果i<=k,那么g[i]=1否则g[i]=g[i+1]*(n-i)/i+n/i

特别注意到f[n]=f[n-1]+1即g[n]=1,可以直接依次算出所有的g

——抄的(PPT)

脑子卡带,g[i]的那个式子我居然推了半天…QvQ

这里写下过程

g[i]=(f[i-1]*i+f[i+1]*(n-i))/n+1-f[i-1]

=(f[i+1]*(n-i)-f[i-1]*(n-i))/n+1

=(n-i)*(f[i+1]-f[i-1])/n+1

=(n-i)*(g[i+1]+g[i])/n+1

所以g[i]=g[i+1]*(n-i)/i+n/i

语文差数学也差的人还有没有活路了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define Mod 100003
#define MAXN 100005
using namespace std;
typedef long long LL;
int n,k,a[MAXN];
LL g[MAXN],f[MAXN];
LL exgcd(int a,int b,LL &d,LL &x,LL &y)
{
    if(!b){d=1,x=1,y=0;return a;}
    else {exgcd(b,a%b,d,y,x);y-=x*(a/b);} 
}
LL inv(LL a)
{
    LL d,x,y;
    exgcd(a,Mod,d,x,y);
    return (x+Mod)%Mod;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    int cnt=0;
    for(int j=n;j>0;j--)
    {
        if(!a[j])continue;
        cnt++;
        for(int i=1;i*i<=j;i++)
        {
            if(j%i==0)
            {
                a[i]^=1;
                if(i*i!=j)a[j/i]^=1;
            }
        }
    }
    for(int i=1;i<=k;i++)f[i]=i;
    g[n]=1;
    for(int i=n;i>k;i--)
    g[i]=(n+(n-i)*g[i+1]%Mod)%Mod*inv(i)%Mod;
    for(int i=k+1;i<=cnt;i++)
    f[i]=(g[i]+f[i-1])%Mod;
    LL ans=1;
    for(int i=1;i<=n;i++)
    ans=(ans*i)%Mod;
    ans=(ans*f[cnt])%Mod;
    printf("%lld\n",ans);
    return 0;
} 

 

posted @ 2017-04-30 14:58  Zars19  阅读(286)  评论(0编辑  收藏  举报