首页 > 代码库 > bzoj4872

bzoj4872

期望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;
}
View Code

 

bzoj4872