【洛谷】P3750 [六省联考 2017] 分手是祝愿

原题链接

题意

B 君在玩一个游戏,这个游戏由 \(n\) 个灯和 \(n\) 个开关组成,给定这 \(n\) 个灯的初始状态,下标为从 \(1\)\(n\) 的正整数。

每个灯有两个状态亮和灭,我们用 \(1\) 来表示这个灯是亮的,用 \(0\) 表示这个灯是灭的,游戏的目标是使所有灯都灭掉。

但是当操作第 \(i\) 个开关时,所有编号为 \(i\) 的约数(包括 \(1\)\(i\))的灯的状态都会被改变,即从亮变成灭,或者是从灭变成亮。

如果当前局面,可以通过操作小于等于 \(k\) 个开关使所有灯都灭掉,那么他将不再随机,直接选择操作次数最小的操作方法(这个策略显然小于等于 \(k\) 步)操作这些开关。

B 君想知道按照这个策略(也就是先随机操作,最后小于等于 \(k\) 步,使用操作次数最小的操作方法)的操作次数的期望。

输出期望乘以 \(n\) 的阶乘对 \(100003\) 取模之后的结果。

\(1 \leq n \leq 10^5, 0 \leq k \leq n\)

思路

虽然灭掉当前节点会影响其他的节点,但不难发现,每个节点无法通过其他的操作组合来取代掉。于是可以考虑从大到小遍历,如果当前灯亮着,那么就把它灭掉,这样做所需的操作次数一定是最少的,且这些编号的灯一定要被直接灭掉。记这样需要的操作次数为 \(m\)

那么当 \(m \leq k\) 时,只需要 \(m\) 步就可以使所有灯灭掉。

而当 \(m > k\) 时,设 \(f_i\) 为从需要 \(i\) 次操作到需要 \(i-1\) 次操作的期望操作次数,那么有 \(\frac{i}{n}\) 的概率选到需要的节点,而有 \(\frac{n-i}{n}\) 的概率选错节点,此时选错的那个节点就还需要额外再按一次。可以得到转移方程:

\[f_i=\frac{i}{n}+\frac{n-i}{n}(f_i+f_{i+1}+1) \]

化简可以得到:

\[f_i=\dfrac{n+(n-i)\times f_{i+1}}{i} \]

那么最终总的期望次数就是 \(k+\sum_{i=k+1}^{m} f_i\)

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10,mod=100003;
int f[N],ans,n,k,tot; bool state[N];
int mul(int a,int b){int res=1;while(b){if(b&1) res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}return res;}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&state[i]);
	for(int i=n;i>=1;i--)
	    if(state[i])
	    {
	    	tot++;
	    	for(int j=1;j*j<=i;j++)
	    	{
	    		if(i%j) continue;
	    		state[j]^=1;if(j*j!=i) state[i/j]^=1;
			}
		}
	for(int i=n;i>=1;i--) f[i]=1ll*(n+1ll*(n-i)*f[i+1]%mod)%mod*mul(i,mod-2)%mod;
	//需要注意f从n开始递推,而不是tot,因为在tot时也有概率按掉不应该按的灯,使需要的操作次数大于tot 
	if(tot<=k) ans=tot;
	else
	{
		for(int i=tot;i>k;i--) ans=(ans+f[i])%mod;
		ans=(ans+k)%mod;
	}
	for(int i=1;i<=n;i++) ans=1ll*ans*i%mod;
	printf("%d\n",ans);
	return 0;
}
posted @ 2023-03-13 16:30  曙诚  阅读(16)  评论(0)    收藏  举报