题目链接
题目解法
首先考虑每个开关会操作的开关的集合是互不相同的,且没有一个集合可以通过其他多个集合拼成
由上述优秀的性质可以知道把所有灯调成
0
0
0 的方法(只考虑每个开关被操作的奇偶性)是唯一的
这样就可以做部分分 k = n k=n k=n,即从 n n n 到 1 1 1 从后往前扫,如果 i i i 开着,就操作 i i i,这样的操作次数就是答案,时间复杂度 O ( n n ) , O(n\sqrt n), O(nn),可以拿到 50 p t s 50pts 50pts
显然考虑期望的线性性
一个比较好的
d
p
dp
dp 表达式是令
f
[
i
]
f[i]
f[i] 表示从需要操作
i
i
i 个开关到需要操作
i
−
1
i-1
i−1 个开关的期望步数
先给出式子:
f
[
i
]
=
n
i
+
n
−
i
n
(
f
[
i
+
1
]
+
f
[
i
]
+
1
)
f[i]=\frac{n}{i}+\frac{n-i}{n}(f[i+1]+f[i]+1)
f[i]=in+nn−i(f[i+1]+f[i]+1)
解释一下,
n
i
\frac{n}{i}
in 是指这一步直接操作一个需要操作的开关的期望
n
−
i
n
(
f
[
i
+
1
]
+
f
[
i
]
+
1
)
\frac{n-i}{n}(f[i+1]+f[i]+1)
nn−i(f[i+1]+f[i]+1) 是指这一步操作一个不必操作的开关,那么需要操作的开关的数量就会
+
1
+1
+1,那么之后操作的过程其实就是从
i
+
1
i+1
i+1 个开关到
i
i
i 个开关,再从
i
i
i 个开关到
i
−
1
i-1
i−1 个开关
化简之后可得
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]
边界是
f
[
n
+
1
]
=
0
f[n+1]=0
f[n+1]=0 或者
f
[
n
]
=
1
f[n]=1
f[n]=1
最后的期望只要把从
1
1
1 到之前求出的把所有灯调成
0
0
0 的步数求和就可以了
不要忘记每一项的 f [ i ] f[i] f[i] 都要乘上 n n n 的阶乘
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N(100100),P(100003);
int n,k,f[N],inv[N];
bool a[N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
int qmi(int a,int b,int p){
int res=1;
for(;b;b>>=1){
if(b&1) res=(LL)res*a%p;
a=(LL)a*a%p;
}
return res;
}
int main(){
n=read(),k=read();
for(int i=1;i<=n;i++) a[i]=read();
int cnt=0;
for(int i=n;i>=1;i--)
if(a[i]){
cnt++;
for(int j=1;j*j<=i;j++)
if(i%j==0){
a[j]^=1;
if(i/j!=j) a[i/j]^=1;
}
}
f[n]=1,inv[0]=1;
for(int i=1;i<=n;i++)
f[n]=(LL)f[n]*i%P,inv[i]=qmi(i,P-2,P);
for(int i=n-1;i;i--)
f[i]=((LL)n*f[n]%P*inv[i]%P+(LL)(n-i)*f[i+1]%P*inv[i]%P)%P;
if(k<cnt){
int ans=k;
for(int i=1;i<=n;i++) ans=(LL)ans*i%P;
for(int i=k+1;i<=cnt;i++)
ans=(ans+f[i])%P;
printf("%d",ans);
}
else{
int ans=cnt;
for(int i=1;i<=n;i++) ans=(LL)ans*i%P;
printf("%d",ans);
}
return 0;
}