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;
    }
  • 相关阅读:
    C/C++多文件之间的变量定义
    PKU POJ 2186 Popular Cows 强连通分量
    重载函数
    ZOJ 2763 Prison Break
    201357 训练赛总结
    hdu 4467 Graph 构造
    201356 训练赛总结
    201353 NEERC 2012, Eastern subregional contest
    2013512 CF 183 总结
    一道动态规划
  • 原文地址:https://www.cnblogs.com/FallDream/p/bzoj4872.html
Copyright © 2011-2022 走看看