标签:bzoj turn isp 需要 从后往前 ret sum 状态 nbsp
期望dp
首先如果k=n的话,那么我们从后往前,只要看到两者的灯就关上,因为如果当前一个灯没关上,那么之后不可能关上,一个灯只能由自己倍数控制,所以这样我们就计算出了需要操作的次数,如果这个次数<=k,直接把这个步数乘上阶乘就可以了。
考虑期望的部分,设f[i]为当前状态下还需要操作i次结束的期望步数,f[i]=(i/n)*(f[i-1]+1)+(1-i/n)*(f[i+1]+1) 就是有i/n的概率会关上一盏不需要关上的灯,期望步数加上还需要i-1步的期望加上走一步乘上概率
但是这个东西只能高斯消元求,肯定跑不过去,那么我们差分一下,g[i]=f[i]-f[i-1]
得出g[i]=(g[i+1]*(n-i)+n)/i
因为g是差分得出的,所以最终答案是g[1]+...+g[tot],tot是最初状态需要的步数。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 100010, mod = 100003; int n, k, tot; int v[N]; ll fac = 1, sum; ll g[N]; ll power(ll x, ll t) { ll ret = 1; for(; t; t >>= 1, x = x * x % mod) if(t & 1) ret = ret * x % mod; return ret; } int main() { scanf("%d%d", &n, &k); for(int i = 1; i <= n; ++i) { scanf("%d", &v[i]); fac = fac * (ll)i % mod; } for(int i = n; i; --i) if(v[i]) { for(int j = 1; j * j <= i; ++j) if(i % j == 0) { v[j] ^= 1; if(j * j != i) v[i / j] ^= 1; } ++tot; } if(k >= tot) { printf("%lld\n", fac * tot % mod); return 0; } g[n + 1] = 1e9; for(int i = n; i; --i) { if(i <= k) g[i] = 1; else g[i] = ((ll)(n - i) * g[i + 1] + n) % mod * power(i, mod - 2) % mod; // sum = (sum + g[i]) % mod; } for(int i = 1; i <= tot; ++i) sum = (sum + g[i]) % mod; sum = sum % mod * fac % mod; printf("%lld\n", (sum + mod) % mod); return 0; }
标签:bzoj turn isp 需要 从后往前 ret sum 状态 nbsp
原文地址:http://www.cnblogs.com/19992147orz/p/7221604.html