1. 程式人生 > >bzoj4872

bzoj4872

還需要 display name 概率 one ide style lose 期望dp

期望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