• P3750 [六省联考2017]分手是祝愿 期望DP


    (color{#0066ff}{ 题目描述 })

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

    (color{#0066ff}{输入格式})

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

    (color{#0066ff}{输出格式})

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

    (color{#0066ff}{输入样例})

    4 0
    0 0 1 1
    

    (color{#0066ff}{输出样例})

    5 0
    1 0 1 1 1
    

    (color{#0066ff}{数据范围与提示})

    对于 (0\%) 的测试点,和样例一模一样;
    对于另外 (30\%) 的测试点,(n leq 10)
    对于另外 (20\%) 的测试点,(n leq 100)
    对于另外 (30\%) 的测试点,(n leq 1000)
    对于 (100\%) 的测试点,(1 leq n leq 100000, 0 leq k leq n)
    对于以上每部分测试点,均有一半的数据满足 (k = n)

    (color{#0066ff}{题解})

    这是一个很好?的期望DP

    每次按下一个开关,只会影响它约数的灯的状态

    预处理出每个开关能改变的灯, 用vector存

    枚举因子是(O(nsqrt{n}))的,因为会有一些浪费(非因子也被枚举了)

    考虑枚举倍数,这样的复杂度是(O(nlogn))的,就可以了

    可以发现,按下当前的按钮,当前的灯的状态一定会改变,而它后面的灯的状态不变

    也就是说,从后往前来,遇到亮的灯就按相应按钮,最多n次,就可以全部熄灭

    而且,可以发现,按按钮的顺序不影响

    所以我们设(f[i]) 代表按(i)次开关可以将灯全部熄灭的状态转移到按(i - 1)次开关将灯全部熄灭的状态的期望步数

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

    n个开关, 有i个是正确的,n-i个是错误的,会增加一个步数,然后从(i + 1 o i-1) 需要(f[i+1]+f[i])次操作

    (f[n]=1)

    如果总操作数都比k小,那么期望就是操作数了

    否则累计从k到总操作数的f值,别忘了最后+k

    还要乘n的阶乘,因为模数是质数还算有点良心,递推时直接乘逆元,最后再乘阶乘就行

    #include<bits/stdc++.h>
    #define LL long long
    LL in() {
    	char ch; int x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    const int mod = 1e5 + 3;
    const int maxn = 1e5 + 10;
    LL ksm(LL x, LL y) {
    	LL re = 1LL;
    	while(y) {
    		if(y & 1) re = re * x % mod;
    		x = x * x % mod;
    		y >>= 1;
    	}
    	return re;
    }
    int b[maxn];
    LL f[maxn], ans;
    using std::vector;
    vector<int> g[maxn];
    int n, k, num;
    signed main() {
    	n = in(), k = in();
    	for(int i = 1; i <= n; i++) b[i] = in();
    	for(int i = 1; i <= n; i++)
    		for(int j = i; j <= n; j += i)
    			g[j].push_back(i);
    	for(int i = n; i >= 1; i--) 
    		if(b[i]) {
    			num++;
    			for(int j = 0; j < (int)g[i].size(); j++) b[g[i][j]] ^= 1;
    		}
    	if(num <= k) ans = num;
    	else {
    		f[n] = 1;
    		for(LL i = n - 1; i >= 1; i--) f[i] = (1LL + ((n - i) * ksm(i, mod - 2) % mod) * (f[i + 1] + 1LL) % mod) % mod;
    		for(int i = num; i > k; i--) (ans += f[i]) %= mod;
    		(ans += k) %= mod;
    	}
    	for(int i = 1; i <= n; i++) (ans *= i) %= mod;
    	printf("%lld", ans);
    	return 0;
    }
    

    考试时看错题了。。。以为是到达小于等于k步的期望,一直不出样例qwq

  • 相关阅读:
    HDU --1251
    POJ -- 2436
    POJ -- 3140
    POJ 3107
    POJ -- 2002
    POJ -- 1655
    lintcode154
    lintcode192
    lintcode582
    lintcode901
  • 原文地址:https://www.cnblogs.com/olinr/p/10220435.html
Copyright © 2020-2023  润新知