题目大意: 给出一排灯泡的状态,每次修改一个灯泡时会将它编号的约数的灯泡的状态同时修改,现在随机修改灯泡,当按照最优策略还有 k k k 次操作就能全灭时就按最优策略,问期望操作次数。
题解
对于一开始的灯泡状态,按照最优策略的操作的话,需要操作的灯泡是固定的。最优策略也不难想,每次操作编号最大的就好,因为编号比他小的都不能更改它的状态,所以它是必定会被操作的。
求出需要操作几次后,就可以开始 d p dp dp 了,设 f [ i ] f[i] f[i] 表示还有 i i i 个灯泡需要操作,转移到 f [ i − 1 ] f[i-1] f[i−1] 期望需要几步。
注意这里不能设 f [ i ] f[i] f[i] 表示还有 i i i 个灯泡需要操作,全部操作完期望需要几步,因为 f [ i ] f[i] f[i] 既能转移到 f [ i + 1 ] f[i+1] f[i+1],又能转移到 f [ i − 1 ] f[i-1] f[i−1],没法递推,所以需要确定递推方向,使它往我们想要的方向推。
那么就有: f [ i ] = 1 + n − i n ( f [ i + 1 ] + f [ i ] ) f[i]=1+\frac {n-i} n(f[i+1]+f[i]) f[i]=1+nn−i(f[i+1]+f[i])。
加号前的 1 1 1 表示这一次操作花费了一个步数,如果成功操作到了需要操作的灯泡,那么就成功转移到了 f [ i − 1 ] f[i-1] f[i−1],没有额外代价,没有操作到的概率为 n − i n \frac {n-i} n nn−i,这样就会使得又多了一个需要操作的灯泡,所以要加上 f [ i + 1 ] f[i+1] f[i+1],然后再加上 f [ i ] f[i] f[i]。
化简得到: f [ i ] = n i + n − i i f [ i + 1 ] f[i]=\frac n i+\frac {n-i} i f[i+1] f[i]=in+in−if[i+1]。
以及,对于 1 ≤ i ≤ k 1\leq i\leq k 1≤i≤k,有 f [ i ] = 1 f[i]=1 f[i]=1。最后把 f [ 1 ] f[1] f[1] 到 f [ 最 优 步 数 ] f[最优步数] f[最优步数] 都加起来就可以了。
代码如下:
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010
#define mod 100003
int n,k,a[maxn],f[maxn];
vector<int> s[maxn];
void work()
{
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j+=i)
s[j].push_back(i);
}
int ksm(int x,int y)
{
int re=1;
while(y)
{
if(y&1)re=1ll*re*x%mod;
x=1ll*x*x%mod;y>>=1;
}
return re;
}
#define inv(x) ksm(x,mod-2)
int ans=0;
int main()
{
scanf("%d %d",&n,&k); work();
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int tot=0; f[n]=1;
for(int i=n;i>=1;i--)if(a[i])
{for(int j=0;j<s[i].size();j++)a[s[i][j]]^=1; tot++;}
if(tot<k)ans=tot;
else
{
for(int i=n-1;i>0;i--)f[i]=1ll*(1ll*n+1ll*(n-i)*f[i+1]%mod)*inv(i)%mod;
for(int i=tot;i>k;i--)ans=(ans+f[i])%mod; ans=(ans+k)%mod;
}
for(int i=1;i<=n;i++)ans=1ll*ans*i%mod;
printf("%d\n",ans);
}