• BZOJ4872: [Shoi2017]分手是祝愿


    4872: [Shoi2017]分手是祝愿

    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

    题解

    送了80分。后20真神。
    我猜这是毕克dalao出的。。
    毕克出NOI2016一道字符串题也送了95的hash分Orz
    希望这次八省联考还有毕克dalao出的题。

    (f_i)为最优(i)步完成转移到最优(i - 1)步完成的期望操作次数。
    有两个结论。
    1、最优方案中,不会按一个点两次以上
    2、最优方案中,按的顺序互换仍然正确
    直观感受一下。

    转移时两种情况。
    1、最优(i)步的时候,有(i)个决策是最优决策,可以转移到(i - 1)步,操作次数为(1)
    2、有(n - i)步不是最优决策,按了之后(操作次数为(1)),最优决策变成了(i + 1)步,需要转回(i)步(期望(f[i + 1])),然后转回(i - 1)步(期望(f[i]))
    等概率选取,(1)的概率是(frac{i}{n})(2)的概率是(frac{n - i}{n})

    得到

    [f_i = frac{i}{n} + (1 - frac{i}{n}) imes (1 + f_{i+1} + f_i), i > k ]

    [f_i = 1, i < k ]

    必须从大到小递推,因为.....
    我问了一下出题人,为什么从小到大不对,他说:

    . . .
    这就是dalao吧

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <map>
    #include <cmath>
    inline long long max(long long a, long long b){return a > b ? a : b;}
    inline long long min(long long a, long long b){return a < b ? a : b;}
    inline long long abs(long long x){return x < 0 ? -x : x;}
    inline void swap(long long &x, long long &y){long long tmp = x;x = y;y = tmp;}
    inline void read(long long &x)
    {
        x = 0;char ch = getchar(), c = ch;
        while(ch < '0' || ch > '9') c = ch, ch = getchar();
        while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
        if(c == '-') x = -x;
    }
    const long long INF = 0x3f3f3f3f;
    const long long MAXN = 1000000 + 10;
    const long long MOD = 100003;
    long long f[MAXN], dp[MAXN], num[MAXN], n, k, mi, sum;
    long long pow(long long a, long long b)
    {
    	long long r = 1, base = a;
    	for(;b;b >>= 1)
    	{
    		if(b & 1) r *= base, r %= MOD;
    		base *= base, base %= MOD;
    	}
    	return r;
    }
    long long ni(long long x)
    {
    	return pow(x, MOD - 2);
    }
    int main()
    {
    	read(n), read(k);
    	f[0] = 1;
    	for(long long i = 1;i <= n;++ i) read(num[i]), f[i] = f[i - 1] * i % MOD;
    	for(long long i = n;i >= 1;-- i)
    		if(num[i])
    		{
    			++ mi;
    			long long ma = sqrt(i);
    			for(long long j = 1;j <= ma;++ j)
    				if(i % j == 0)
    				{
    					if(j * j == i) num[j] ^= 1;
    					else num[j] ^= 1, num[i / j] ^= 1;
    				}
    		}
    	if(k >= mi)
    	{
    		printf("%lld
    ", mi * f[n] % MOD);
    		return 0;
    	}
    	for(int i = 1;i <= k;++ i) dp[i] = 1; 
    	dp[n] = 1;
    	for(int i = n - 1;i > k;-- i)
    		dp[i] = ((((n - i) * dp[i + 1]) % MOD + n) % MOD * ni(i)) % MOD;
    	for(int i = mi;i >= 1;-- i) sum += dp[i], sum %= MOD;
    	sum *= f[n];
    	sum %= MOD; 
    	printf("%lld", sum);
        return 0;
    }
    
  • 相关阅读:
    南阳33(蛇形填数)规律题;
    南阳241(字母统计)
    南阳57(6174问题)
    android图形基础知识
    Linux中yum手动安装、手动建立仓库文件夹关联实现关联包自动安装、yum相关命令使用
    debug连线指令
    Qt之信号连接,你Out了吗?
    hdu-4607-Park Visit
    MySQL 分区表 partition线上修改分区字段,后续进一步学习partition (1)
    如何用正则将多个空格看成一个空格结合spllit()方法将文本数据入库
  • 原文地址:https://www.cnblogs.com/huibixiaoxing/p/8516608.html
Copyright © 2020-2023  润新知