@ysner
2018-03-30T16:20:57.000000Z
字数 1946
阅读 2424
期望
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;}
