正题
题目链接:https://www.luogu.org/problem/P3750
题目大意
nnn盏灯和按钮,每次随机选择一个xxx按下后会让xxx的倍数的灯都取反,然后若最少kkk步就可以将所有灯关闭那么直接选择最优策略,求关闭所有灯的期望次数。
解题思路
做期望dpdpdp,设fif_ifi表示从i+1i+1i+1个需要按下的到iii个需要按下的时的期望次数,有转移方程
fi=in+n−in(fi+1+fi+1)f_i=\frac{i}{n}+\frac{n-i}{n}(f_{i+1}+f_{i}+1)fi=ni+nn−i(fi+1+fi+1)
然后化简为
fi=n+(n−i)∗fi+1if_i=\frac{n+(n-i)*f_{i+1}}{i}fi=in+(n−i)∗fi+1
然后我们考虑灯的性质,我们发现有些灯是必须按下的,而其他的灯都是不能按下的。所有我们可以计算出一个数numnumnum表示有多少按钮必须按下,这时我们可以发现答案就是(∑i=k+1numfi)+k(\sum_{i=k+1}^{num}f_i)+k(∑i=k+1numfi)+k,当然要特判一下num≤knum\leq knum≤k的情况。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=110000,XJQ=100003;
ll n,a[N],f[N],num,ans,k;
ll power(ll x,ll b)
{ll ans=1;while(b){if(b&1) ans=ans*x%XJQ;x=x*x%XJQ;b>>=1;}return ans;
}
int main()
{scanf("%lld%lld",&n,&k);for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);for(ll i=n;i>=1;i--)if(a[i]){num++;for(ll j=1;j*j<=i;j++)if(!(i%j)){a[j]^=1;if(j*j!=i) a[i/j]^=1;}}f[n+1]=1;for(ll i=n;i>=1;i--)f[i]=((n-i)*f[i+1]%XJQ+n)%XJQ*power(i,XJQ-2)%XJQ;if(num<=k) ans=num;else{for(ll i=k+1;i<=num;i++)(ans+=f[i])%=XJQ;(ans+=k)%=XJQ;}for(ll i=1;i<=n;i++)ans=ans*i%XJQ;printf("%lld",ans);
}