题目
展开
题目描述
Zeit und Raum trennen dich und mich. 时空将你我分开。
B 君在玩一个游戏,这个游戏由 nn 个灯和 nn 个开关组成,给定这 nn 个灯的初始状态,下标为从 11 到 nn 的正整数。
每个灯有两个状态亮和灭,我们用 11 来表示这个灯是亮的,用 00 表示这个灯是灭的,游戏的目标是使所有灯都灭掉。
但是当操作第 ii 个开关时,所有编号为 ii 的约数(包括 11 和 ii)的灯的状态都会被改变,即从亮变成灭,或者是从灭变成亮。
B 君发现这个游戏很难,于是想到了这样的一个策略,每次等概率随机操作一个开关,直到所有灯都灭掉。
这个策略需要的操作次数很多,B 君想到这样的一个优化。如果当前局面,可以通过操作小于等于 kk 个开关使所有灯都灭掉,那么他将不再随机,直接选择操作次数最小的操作方法(这个策略显然小于等于 kk 步)操作这些开关。
B 君想知道按照这个策略(也就是先随机操作,最后小于等于 kk 步,使用操作次数最小的操作方法)的操作次数的期望。
这个期望可能很大,但是 B 君发现这个期望乘以 nn 的阶乘一定是整数,所以他只需要知道这个整数对 100003100003 取模之后的结果。
输入格式
第一行两个整数 n, kn,k。 接下来一行 nn 个整数,每个整数是 00 或者 11,其中第 ii 个整数表示第 ii 个灯的初始情况。
输出格式
输出一行,为操作次数的期望乘以 nn 的阶乘对 100003100003 取模之后的结果。
输入输出样例
输入 #1复制
4 0
0 0 1 1
输出 #1复制
512
输入 #2复制
5 0
1 0 1 1 1
输出 #2复制
5120
说明/提示
对于 0%0% 的测试点,和样例一模一样;
对于另外 30%30% 的测试点,n \leq 10n≤10;
对于另外 20%20% 的测试点,n \leq 100n≤100;
对于另外 30%30% 的测试点,n \leq 1000n≤1000;
对于 100%100% 的测试点,1 \leq n \leq 100000, 0 \leq k \leq n1≤n≤100000,0≤k≤n;
对于以上每部分测试点,均有一半的数据满足 k = nk=n。
思路
考虑期望,设f[i]表示从有i个正确选择变为有i-1个正确选择的期望操作次数,
f
[
i
]
=
i
n
+
(
1
−
i
n
)
∗
(
1
+
f
[
i
+
1
]
+
f
[
i
]
)
f[i]=\dfrac{i}{n}+(1-\dfrac{i}{n})*(1+f[i+1]+f[i])
f[i]=ni+(1−ni)∗(1+f[i+1]+f[i])
(n个开关,i个正确,其他n-i个会增加一个错误,需f[n+1]+f[n]次操作变到i-1)
移项
f
[
i
]
=
1
+
(
1
∗
n
−
i
)
∗
(
f
[
i
+
1
]
+
1
)
i
f[i]=1+\dfrac{(1*n-i)*(f[i+1]+1)}{i}
f[i]=1+i(1∗n−i)∗(f[i+1]+1),
f[n] = 1,把f累加一下就可以。
不要忘了乘
n
!
n!
n!
代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long LL;
inline void read(int &x) {
static char c; bool flag = 0;
while((c=getchar())<'0'||c>'9') flag |= (c=='-');
x=c-'0';while((c=getchar())>='0'&&c<='9') x = x*10+c-'0';
flag?x=-x:x;
}
typedef long long LL;
const int p = 100003;
int n,k,b[101000];
LL f[101000],inv[101000];
vector<int> g[101000];
void work() {
LL ans = 0,tp = 0;
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j += i)
g[j].push_back(i);
for (int i = n; i >= 1; i--)
if (b[i]) {
for (int j = 0; j < g[i].size(); j++) b[g[i][j]] ^= 1;
tp++;
}
if (tp <= k) ans = tp;
else {
f[n] = 1;
for (int i = n-1; i > 1; i--) f[i] = (1LL+(1LL*n-i)*(f[i+1]+1)*inv[i])%p;
for (int i = tp; i > k; i--) ans = (ans+f[i])%p;
ans = (ans+k)%p;
}
for (int i = 1; i <= n; i++) ans = (ans*i)%p;
printf("%lld",ans);
}
int main() {
read(n); read(k);
for (int i = 1; i <= n; i++) read(b[i]);
inv[1] = 1;
for (int i = 2; i <= n; i++) inv[i] = ((p-p/i)*inv[p%i])%p;
work();
return 0;
}