[六省联考2017]分手是祝愿

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

Hint:

(n le 10^6​)

Solution:

首先考虑最优策略,就是从大到小能关则关

这里大概理解一下,一来如果按一个关着的开关必定不优,因为你虽然关了它的因数,但现在又要按他的倍数,所以它的因数又会开

同时我们发现按小的一定不能影响大的,而如果按这个小的比较优,我们从大到小也一定会先按到它

也就是说最优策略唯一确定,要按的开关可以预处理出来(不一定一来就是开着的)

所以我们如果按到一个不是最优策略的开关,就相当于你要多按一次按回来

我们设(dp[i])表示按最优策略还剩i个开关要按到还剩i-1个的期望步数,则有:

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

直接从(n)开始递推,因为最多按(n)

对于答案,大于k时累加(f[i]),小于k的时候我们就直接把答案加上最优策略的步数

至于因数,用vector预处理一下

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1 
#define rs p<<1|1
using namespace std;
typedef long long ll;
const ll mxn=2e5+5,mod=100003;
ll n,k,cnt,a[mxn],dp[mxn],inv[mxn];
inline ll read() {
	char c=getchar(); ll x=0,f=1;
	while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
	return x*f;
}
inline ll chkmax(ll &x,ll y) {if(x<y) x=y;}
inline ll chkmin(ll &x,ll y) {if(x>y) x=y;}

vector<ll > vec[mxn];

int main()
{
	n=read(); k=read(); inv[1]=1; dp[n]=1;
	for(ll i=2;i<=n;++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(ll i=1;i<=n;++i) a[i]=read();
	for(ll i=1;i<=n;++i) 
		for(ll j=i;j<=n;j+=i) 
			vec[j].push_back(i);
	for(ll i=n;i>=1;--i) {
		if(!a[i]) continue ;
		for(ll j=0;j<vec[i].size();++j) 
			a[vec[i][j]]^=1;
		++cnt;	
	}		
	for(ll i=n-1;i>k;--i) dp[i]=1ll*(n+(n-i)*dp[i+1]%mod)%mod*inv[i]%mod;
	for(ll i=k;i>=1;--i) dp[i]=1; ll ans=0;
	for(ll i=1;i<=cnt;++i) ans=(ans+dp[i])%mod;
	for(ll i=1;i<=n;++i) ans=1ll*ans*i%mod;
	printf("%d",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/list1/p/10527188.html