zoukankan      html  css  js  c++  java
  • [bzoj4872][Shoi2017]分手是祝愿

    来自FallDream的博客,未经允许,请勿转载,谢谢。


    B 君在玩一个游戏,这个游戏由 n 个灯和 n 个开关组成,给定这 n 个灯的初始状态,下标为从 1 到 n 的正整数。每个灯有两个状态亮和灭,我们用 1 来表示这个灯是亮的,用 0 表示这个灯是灭的,游戏的目标是使所有灯都灭掉。但是当操作第 i 个开关时,所有编号为 i 的约数(包括 1 和 i)的灯的状态都会被改变,即从亮变成灭,或者是从灭变成亮。B 君发现这个游戏很难,于是想到了这样的一个策略,每次等概率随机操作一个开关,直到所有灯都灭掉。这个策略需要的操作次数很多, B 君想到这样的一个优化。如果当前局面,可以通过操作小于等于 k 个开关使所有灯都灭掉,那么他将不再随机,直接选择操作次数最小的操作方法(这个策略显然小于等于 k 步)操作这些开关。B 君想知道按照这个策略(也就是先随机操作,最后小于等于 k 步,使用操作次数最小的操作方法)的操作次数的期望。这个期望可能很大,但是 B 君发现这个期望乘以 n 的阶乘一定是整数,所以他只需要知道这个整数对 100003 取模之后的结果。
    n<=100000
     
    题目名剧毒
    首先发现关掉灯的方案是唯一的 因为每个都只和比他大的有关系,所以从大到小一一确定即可,复杂度nlogn
    所以这道题首先暴力找出有几个要改,如果小于k,输出k乘以n! 就好了  这个居然就有80分。
     
    /以下是瞎搞
    剩下的很容易想到一个dp,f[i][j]表示走i步有j个要改的方案,这样每个i转移是O(n)的
    后面的贡献非常小  longdouble统计答案,多转移几次啥的说不定能拿到一点分数?
    (确实成功拿到了5分)
    以上是瞎搞/
     
    用f[i]表示从有i个要改的状态出发,走到f[i-1]这个状态的期望次数,有
    $f[i]=frac{n-i}{n}*(f[i]+f[i+1])+1$
    整理得到$f[i]=frac{n+(n-i)f[i+1]}{i}$
    递推即可,边界f[n]=1  复杂度O(n)
    答案是$sum_{i=k+1}^{p}f[i] * n!$ 其中p是一开始要改的数量。
    复杂度nlogn
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #define MN 100000
    #define mod 100003
    #define ld long double
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
    
    vector<int> v[MN+5];
    int n,k,s[MN+5],sum=0,inv[MN+5],f[MN+5],ans=0;
    
    int main()
    {
        n=read();ans=k=read();
        for(int i=1;i<=n;++i) s[i]=read();
        for(int i=1;i<=n;++i)
            for(int j=i;j<=n;j+=i) v[j].push_back(i);
        for(int i=n;i;--i)
            if(s[i]) 
            {
                s[i]=0,++sum; 
                for(int j=0;j<v[i].size();++j)
                    s[v[i][j]]^=1;
            }
        if(sum<=k) 
        {
            for(int i=1;i<=n;++i) sum=1LL*sum*i%mod;
            return 0*printf("%d
    ",sum); 
        } 
        inv[0]=inv[1]=f[n]=1;
        for(int i=2;i<=n;++i) inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
        for(int i=n-1;i>k;--i)
            f[i]=1LL*(n+1LL*(n-i)*f[i+1]%mod)*inv[i]%mod;
        for(int i=sum;i>k;--i) (ans+=f[i])%=mod;
        for(int i=1;i<=n;++i) ans=1LL*ans*i%mod;
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    Python量化分析,计算KDJ
    Ubuntu16.04安装Python3.6 和pip(python3 各版本切换)
    使用docker加载已有镜像安装Hyperledger Fabric v1.1.0
    Ubuntu 16.04将左侧面板置于底部
    解决Flask局域网内访问不了的问题
    Ubuntu 16.04 安装Go 1.9.2
    Ubuntu16.04下安装Hyperledger Fabric 1.0.0
    Ubuntu 16.04安装Docker-CE
    用Python抓取网页并解析
    图解python中赋值、浅拷贝、深拷贝的区别
  • 原文地址:https://www.cnblogs.com/FallDream/p/bzoj4872.html
Copyright © 2011-2022 走看看