题目:
https://www.luogu.org/problemnew/show/P3750
分析:
显然按一个开关不能使得比他大的数熄灭。所以最优方案一定是每次选出最大的数按掉。
可以用枚举倍数,然后用vector存某个数的约数,求出需要按的开关数
c
n
t
cnt
cnt。
设
f
[
i
]
f[i]
f[i]表示还有
i
i
i个开关需要按到还有
i
−
1
i-1
i−1个开关需要按的情况期望需要多少步,
显然
f
[
i
]
=
i
n
+
n
−
i
n
∗
(
f
[
i
+
1
]
+
f
[
i
]
+
1
)
f[i]=\frac{i}{n}+\frac{n-i}{n}*(f[i+1]+f[i]+1)
f[i]=ni+nn−i∗(f[i+1]+f[i]+1)
前面的表示直接按到了需要按的开关,后面表示没有按到,那么就需要按
i
+
1
i+1
i+1个开关到
i
i
i,再从
i
i
i到
i
−
1
i-1
i−1。
其中,
f
[
n
]
=
1
f[n]=1
f[n]=1,若
i
<
=
k
i<=k
i<=k,
f
[
i
]
=
1
f[i]=1
f[i]=1。
答案就是
∑
i
=
1
c
n
t
f
[
i
]
\sum_{i=1}^{cnt}f[i]
∑i=1cntf[i]。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#define LL long long
const int maxn=1e5+7;
const LL mod=100003;
using namespace std;
int n,k,cnt;
int a[maxn];
LL f[maxn],ans;
vector <int> p[maxn];
LL ksm(LL x,LL y)
{
if (y==1) return x;
LL c=ksm(x,y/2);
c=c*c%mod;
if (y&1) c=c*x%mod;
return c;
}
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=n;i++)
{
for (int j=i;j<=n;j+=i)
{
p[j].push_back(i);
}
}
for (int i=n;i>0;i--)
{
if (a[i])
{
cnt++;
for (int j=0;j<p[i].size();j++) a[p[i][j]]^=1;
}
}
f[n]=1;
for (int i=n-1;i>0;i--)
{
f[i]=(f[i+1]*(LL)(n-i)+(LL)n)%mod*ksm(i,mod-2)%mod;
}
if (cnt<=k) ans=cnt;
else
{
for (int i=k+1;i<=cnt;i++) ans=(ans+f[i])%mod;
ans=(ans+k)%mod;
}
for (int i=1;i<=n;i++) ans=ans*(LL)i%mod;
printf("%lld\n",ans);
}