关灯游戏
金牌导航 期望-8
题目大意
有n盏灯,有些是亮的,有的是暗的,现在如果按一个位置的开关,那么是它因数的位置的灯都会改变开关情况,现在如果用k步不能直接关完,就随机按,直到可以k步关完,就用最小的步数,问你期望步数
样例输入
4 0
0 0 1 1
样例输出
512
样例输入
5 0
1 0 1 1 1
样例输出
5120
数据范围
1⩽n⩽105,0⩽k⩽n1\leqslant n\leqslant 10^5,0\leqslant k\leqslant n1⩽n⩽105,0⩽k⩽n
解题思路
对于第n个灯,只有第n个开关可以控制它,那么第n个开关必须使第n个灯熄灭,熄灭后就不能再动了
对于前面的灯,后面的开关都不能动,所以只有当前开关可以控制当前灯,所以必须使其关闭
以此类推,对于每一个位置,都有其按或不按的选择
记我们最优按sum次可以直接熄灭
可以从大到小暴力求要sum
设f_i为要再按i个开关时,当前开关按对的期望值
那么有:
fi=in+n−in×(1+fi+1+fi)f_i=\frac{i}{n}+\frac{n-i}{n}\times (1+f_{i+1}+f_i)fi=ni+nn−i×(1+fi+1+fi)
in\frac{i}{n}ni是i个开关中按对一个的期望
n−in×(1+fi+1+fi)\frac{n-i}{n}\times (1+f_{i+1}+f_i)nn−i×(1+fi+1+fi)是有n−in\frac{n-i}{n}nn−i的概率按错,如果按错了,就先计算到i+1的1步,然后走回来要加fi+1f_{i+1}fi+1,再加上想要按到i-1的期望fif_ifi
fi=in+n−in×(1+fi+1+fi)fi=in+n−in+n−in×fi+1+n−in×fifi×(1−n−in)=in+n−in+n−in×fi+1in×fi=in+n−in+n−in×fi+1fi=i+n−i+(n−i)×fi+1ifi=n+(n−i)×fi+1i\begin{aligned} f_i&=\frac{i}{n}+\frac{n-i}{n}\times (1+f_{i+1}+f_i)\\ \\ f_i&=\frac{i}{n}+\frac{n-i}{n}+\frac{n-i}{n}\times f_{i+1}+\frac{n-i}{n}\times f_i\\\\ f_i\times(1-\frac{n-i}{n})&=\frac{i}{n}+\frac{n-i}{n}+\frac{n-i}{n}\times f_{i+1}\\\\ \frac{i}{n}\times f_i&=\frac{i}{n}+\frac{n-i}{n}+\frac{n-i}{n}\times f_{i+1}\\\\ f_i&=\frac{i+n-i+(n-i)\times f_{i+1}}{i}\\\\ f_i&=\frac{n+(n-i)\times f_{i+1}}{i}\\ \end{aligned}fififi×(1−nn−i)ni×fififi=ni+nn−i×(1+fi+1+fi)=ni+nn−i+nn−i×fi+1+nn−i×fi=ni+nn−i+nn−i×fi+1=ni+nn−i+nn−i×fi+1=ii+n−i+(n−i)×fi+1=in+(n−i)×fi+1
那么就得到了关于f的递推式
当递推到k时,直接加k就行了
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define wyc 100003
#define N 100010
using namespace std;
ll n, k, ans, sum, a[N], f[N], p[N];
ll Counting(ll x, ll y)
{ll z = 1;while(y){if (y&1) z = z * x % wyc;x = x * x % wyc;y >>= 1;}return z;
}
int main()
{scanf("%lld%lld", &n, &k);for (ll i = 1; i <= n; ++i)scanf("%lld", &a[i]);for (ll i = n; i > 0; --i){for (ll j = 2; i * j <= n; ++j)//它的倍数按了,它也要按a[i] ^= p[i * j];if (a[i])//要再按{p[i] = 1;sum++;}}if (sum <= k) ans = sum;//如果小于直接最优解else{f[n] = 1;if (n == sum) ans = (ans + 1) % wyc;for (ll i = n - 1; i > k; --i){f[i] = (n + (n - i) * f[i + 1] % wyc) * Counting(i, wyc - 2) % wyc;//递推if (i <= sum) ans = (ans + f[i]) % wyc;//计算期望值}ans = (ans + k) % wyc;//小于等于k,直接最优解}for (ll i = 1; i <= n; ++i)ans = ans * i % wyc;//乘阶乘printf("%lld", ans);return 0;
}