zoukankan      html  css  js  c++  java
  • [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
    ",ans);
        return 0;
    } 
  • 相关阅读:
    VB中 参数不可选
    VB中 “实时错误'-2147217887” 和 “编译错误:无效限定符”
    VB中 文本框的ScrollBars属性不管用
    VB中 “编译错误:未找到方法或数据成员””和“实时错误'424'”
    【读书笔记】之《逻辑思维》
    【My SQL】常见语句
    【自考】《数据库系统原理》之键、主键、超键等概念
    Python接口测试实战1(下)- 接口测试工具的使用
    Python接口测试实战1(上)- 接口测试理论
    为应用程序池 ''DefaultAppPool'' 提供服务的进程意外终止。进程 ID 是 ''xxx''问题的解决方法
  • 原文地址:https://www.cnblogs.com/Zars19/p/6789469.html
Copyright © 2011-2022 走看看