题意:给定 $n$ 个灯的初始开/关状态,每次可以随机选择一个点进行操作,当操作第 $i$ 个开关时,所有 $i$ 的约数的灯的状态都会改变. 当操作到局面最少小于等于 $k$ 步就能将所有灯灭掉时就不用随机,而直接选取最优方案.
求:让所有灯全部灭掉的期望操作次数.
加入给定一个局面,那么最优解一定是从编号大的灯向编号小的灯开始操作(因为小灯管不了大灯)
这样,你在省选的时候能拿到 $50pts$ 的暴力分~
然而,分析到这一步就毫无思路了~
但是呢,我们发现这道题中最小操作次数非常关键:如果最小操作次数小于等于 $k$ 就可以直接操作而不再随机了.
即我们可以只考虑当前局面最优操作次数,从而在最优操作次数上做文章.
有一个性质:如果操作编号是固定的,那么先后顺序是无所谓的.
假设当前最优操作次数为 $a$,即 $n$ 个编号中有 $a$ 个会使局面更优的操作点,那么其余点都会使局面更差.
想到这里,我们令 $f[i]$ 表示从最优操作次数为 $i$ 的局面到最优操作次数为 $(i-1)$ 的操作次数.
则有 $f[i]=(f[i]+f[i+1]) imes frac{n-i}{n}+1 imes frac{i}{n}$,整理得:$f[i]=f[i+1] imes frac{n-i}{i}+1$
最后在计算答案是只需从初始局面所对应得最优移动步数开始累计即可.
#include <bits/stdc++.h> #define N 100005 #define mod 100003 #define LL long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; LL fac[N],inv[N],dp[N]; int sta[N],v[N]; LL qpow(LL x,LL y) { LL tmp=1ll; for(;y;y>>=1,x=x*x%mod) if(y&1) tmp=tmp*x%mod; return tmp; } LL Inv(LL x) { return qpow(x,mod-2); } int main() { // setIO("input"); int i,j,n,k,stp=0; scanf("%d%d",&n,&k); fac[0]=inv[0]=1ll; for(i=1;i<=n;++i) fac[i]=fac[i-1]*1ll*i%mod,inv[i]=Inv(fac[i]); for(i=1;i<=n;++i) scanf("%d",&sta[i]); for(i=n;i>=1;--i) { sta[i]^=v[i]; if(sta[i]) { ++stp; for(j=1;j*j<=i;++j) if(i%j==0) { v[j]^=1; if(j!=i/j) v[i/j]^=1; } } } // printf("%d ",stp); // dp[n]=1; for(i=n;i>=1;--i) dp[i]=(dp[i+1]*1ll*(n-i)%mod+n)%mod*qpow(i,mod-2)%mod; LL ans=0ll; if(stp<=k) ans=stp; else { for(i=stp;i>k;--i) ans=(ans+dp[i])%mod; ans=(ans+k)%mod; } for(i=1;i<=n;++i) ans=ans*1ll*i%mod; printf("%lld ",ans); return 0; }