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

https://zybuluo.com/mdeditor#1094623

标签(空格分隔): 期望


题面

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分???哪还有谁会打正解呢???

显然的期望公式(逆推,包括后继状态,(E_i)代表最快还要走(i)步时的期望)
(E_i=frac{i}{n}E_{i-1}+frac{n-i}{n}E_{i+1}+1)
没法递推啊。。。

那么差分一下如何?((g_i)代表公差)
(sum_{j=1}^ig_j=frac{i}{n}sum_{j=1}^{i-1}g_j+frac{n-i}{n}sum_{j=1}^{i+1}g_j+1)
自己再化化简便得
(g_i=frac{(n-i)g_{i+1}+n}{i})

最后注意逆元不是素数,所以让我们回顾一下逆元线性递推公式。
(inv[i]=(p-p/i)* inv[pmod i](p为取余数))

// 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
",i,j);
    ysn[i].push_back(j);
    if(j!=i/j) ysn[i].push_back(i/j);//printf("%d
",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
",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
",ans*jc%mod);
  return 0;
}
原文地址:https://www.cnblogs.com/yanshannan/p/8679541.html