• [bzoj4872]分手是祝愿


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

    Input

    第一行两个整数 n, k。
    接下来一行 n 个整数,每个整数是 0 或者 1,其中第 i 个整数表示第 i 个灯的初始情况。
    1 ≤ n ≤ 100000, 0 ≤ k ≤ n;

    Output

    输出一行,为操作次数的期望乘以 n 的阶乘对 100003 取模之后的结果。

    Sample Input

    4 0
    0 0 1 1

    Sample Output

    512
     
    题解:
    熟悉的题目啊...(17年省选打酱油的回忆)这个题的灵魂就是期望式子的推导
    我们先来考虑不随机的情况:
    我们应该从大编号往小编号(因为大编号只能按自己才能改变)一路按过去
    这样模拟一遍我们就可以算出来正常情况下要按多少次,设这个次数为num
    那么,如果考虑等概率瞎按的情况呢?
    我们设f[i]为从"剩下i个位置亮按到到剩下i-1个位置亮"需要按的次数
    那么有2种情况
    1°i/n的概率按到一个应该按的灯,直接成功
    2°(n-i)/n的概率按到一个不应该按的灯,这时先要按回来到i,再按到i-1
    所以f[i]=i/n+(n-i)/n*(b[i+1]+b[i]+1),再给它大力化简一下,把f[i]放在一边
    得到:f[i]=(f[i+1]*(n-i)+n)/i
    求出来之后,我们就要统计(k,num]间num的累加之和,再乘上阶乘就完事了,ans=Σ{f[i],i∈(k,num]}*n!
    如果n==k或者k>num,一上来就直接最优解,不用随机,这时ans=num*n!
    在计算的时候处理上取模(逆元之类的),就可以A掉这道题了
    代码见下:
    #include<cstring>
    #include<cstdio>
    using namespace std;
    typedef long long LL;
    const int N=100010;
    const int mod=100003; 
    int n,k,a[N];
    LL f[N],num;
    LL quick_mod(LL di,int mi)
    {
        LL ret=1;
        while(mi)
        {
            if(mi&1)ret=ret*di%mod;
            di=di*di%mod;
            mi>>=1;
        }
        return ret;
    }
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=n;i>=1;i--)
            if(a[i])
            {
                for(int j=1;j*j<=i;j++)
                    if(!(i%j))
                    {
                        a[j]^=1;
                        if(j*j!=i)a[i/j]^=1;
                    }
                num++;
            }
        for(LL i=n;i>=1;i--)
            f[i]=(f[i+1]*(n-i)%mod+n)%mod*quick_mod(i,mod-2)%mod;
        LL t=0;
        if(num<k||n==k)t=num;
        else
        {
            for(LL i=num;i>k;i--)
                t=(t+f[i])%mod; 
            t=(t+k)%mod; 
        }
        for(LL i=1;i<=n;i++)
            t=t*i%mod;
        printf("%lld",t);
    }
    bzoj4872
     
     
    Progress is not created by contented people.
  • 相关阅读:
    使用AsyncTask简单的文件遍历
    如何创建黑名单
    关于PopupMenu的用法
    联系人的搜索和添加
    php获取真实IP地址
    socket.io获取客户端的IP地址(修正官方1.0.4版本BUG)
    ubuntu下安装bind9
    install source nginx 1.6.0
    在Ubuntu下的samba实现
    买了新硬盘,给ESXI的虚拟机挂进真实硬盘。
  • 原文地址:https://www.cnblogs.com/LadyLex/p/7002977.html
Copyright © 2020-2023  润新知