@ysner
2018-03-31T00:20:57.000000Z
字数 1946
阅读 1944
期望
B 君在玩一个游戏,这个游戏由 n 个灯和 n 个开关组成,给定这 n 个灯的初始状态,下标为从 1 到 n 的正整数。
每个灯有两个状态亮和灭,我们用 1 来表示这个灯是亮的,用 0 表示这个灯是灭的,游戏的目标是使所有灯都灭掉。
但是当操作第 i 个开关时,所有编号为 i 的约数(包括 1 和 i)的灯的状态都会被改变,即从亮变成灭,或者是从灭变成亮。
B 君发现这个游戏很难,于是想到了这样的一个策略,每次等概率随机操作一个开关,直到所有灯都灭掉。
这个策略需要的操作次数很多, B 君想到这样的一个优化。如果当前局面,可以通过操作小于等于 k 个开关使所有灯都灭掉,那么他将不再随机,直接选择操作次数最小的操作方法(这个策略显然小于等于 k 步)操作这些开关。
B 君想知道按照这个策略(也就是先随机操作,最后小于等于 k 步,使用操作次数最小的操作方法)的操作次数的期望。
这个期望可能很大,但是 B 君发现这个期望乘以 n 的阶乘一定是整数,所以他只需要知道这个整数对 100003 取模之后的结果。
震惊,X省省选怒送80分,让无题不毒瘤的HNOI情何以堪。。。
据说随便贪心从大到小枚举就有80分???哪还有谁会打正解呢???
显然的期望公式(逆推,包括后继状态,代表最快还要走步时的期望)
没法递推啊。。。
那么差分一下如何?(代表公差)
自己再化化简便得
最后注意逆元不是素数,所以让我们回顾一下逆元线性递推公式。
// luogu-judger-enable-o2
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#define ll long long
#define re register
#define il inline
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int mod=100003;
int n,m,k,a[100005],dp[100005],inv[100005],b;
vector<int>ysn[100005];
ll ans,jc=1;
il int gi()
{
re int x=0,t=1;
re char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') t=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*t;
}
il ll ksm(re ll S,re ll n)
{
re ll T=S;S=1;
while(n)
{
if(n&1) S=S*T%mod;
T=T*T%mod;
n>>=1;
}
return S;
}
int main()
{
n=gi();k=gi();
fp(i,1,n) a[i]=gi(),(jc*=i)%=mod;
fp(i,1,n)
fp(j,1,sqrt(i))
if(i%j==0)
{//printf("%d %d\n",i,j);
ysn[i].push_back(j);
if(j!=i/j) ysn[i].push_back(i/j);//printf("%d\n",i/j);}
}
fq(i,n,1)
{
if(!a[i]) continue;
for(re int j=0;j<ysn[i].size();j++)
a[ysn[i][j]]=1-a[ysn[i][j]];
++b;
}
if(b<=k) return printf("%lld\n",b*jc%mod),0;
dp[n]=1;inv[1]=1;
fp(i,2,n) inv[i]=(mod-1ll*(mod/i)*inv[mod%i]%mod)%mod;
fq(i,n-1,k+1) dp[i]=(1ll*(n-i)*dp[i+1]%mod+n)%mod*inv[i]%mod;
fp(i,1,b) ans+=((i>k)?dp[i]:1);
printf("%lld\n",ans*jc%mod);
return 0;
}