zoukankan      html  css  js  c++  java
  • BZOJ4872 [六省联考2017]分手是祝愿 【期望dp】

    题目

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

    输入格式

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

    输出格式

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

    输入样例

    4 0

    0 0 1 1

    输出样例

    512

    题解

    为使操作次数最少,因为小的不能影响大的,那么从大的开始关闭,要关就一定关,因为往前没有灯可以关闭当前灯,所以是正确的
    这样一枚举就是(O(nlogn)),预处理出了初始局面所需步数

    然后就期望dp
    期望dp一般设为当前状态到目标状态或者下一个状态的期望步数
    我们设(f[i])表示还剩(i)个操作就完成的情况下,到剩余(i - 1)个操作就完成所需要的操作期望次数
    我们有(frac{i}{n})的概率操作正确
    另有(frac{n - i}{n})的概率操作失败,此时我们就相当于多了一个操作撤回这次失败,就回到了(f[i + 1])

    所以我们有

    [f[i] = frac{i}{n} + frac{n - i}{n} * (1 + f[i + 1] + f[i]) ]

    化简得

    [f[i] = frac{n}{i} + frac{n - i}{i} * f[i + 1] ]

    递推一下就可以算出(f[i])

    由于还剩(k)步开始直接操作(k)步结束,我们令(f[1...k] = 1)
    如果初始局面需要步数为(m)

    [ans = sumlimits_{i = 1}^{m} f[i] ]

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
    using namespace std;
    const int maxn = 100005,maxm = 100005,INF = 1000000000,P = 100003;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    int f[maxn],shut[maxn],a[maxn],inv[maxn],n,k,need;
    int main(){
    	n = read(); k = read();
    	REP(i,n) a[i] = read();
    	for (int i = n; i; i--){
    		for (int j = i + i; j <= n; j += i)
    			if (shut[j]) a[i] ^= 1;
    		if (a[i]) shut[i] = true,need++;
    	}
    	inv[0] = inv[1] = 1;
    	for (int i = 2; i <= n; i++) inv[i] = 1ll * (P - P / i) * inv[P % i] % P;
    	f[n] = 1;
    	for (int i = n - 1; i; i--){
    		f[i] = (1ll * n * inv[i] % P + 1ll * (n - i) * inv[i] % P * f[i + 1] % P) % P;
    	}
    	for (int i = 1; i <= k; i++) f[i] = 1;
    	int ans = 0;
    	for (int i = 1; i <= need; i++) ans = (ans + f[i]) % P;
    	for (int i = 1; i <= n; i++) ans = 1ll * ans * i % P;
    	printf("%d
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    解决Windows 10每次重启默认浏览器都被重置为IE的一个办法
    使用cookie登录百度网盘账号
    【C++ Primer | 14】重载运算
    Visual Studio Code 快捷键的使用
    【C++ Primer | 8】IO库
    Git push常见用法
    Git 基础
    Git连接GitHub仓库详解
    ceph关于rpm包构建的教程
    monitor综合
  • 原文地址:https://www.cnblogs.com/Mychael/p/8964131.html
Copyright © 2011-2022 走看看