1. 程式人生 > >LOJ #2145. 「SHOI2017」分手是祝願

LOJ #2145. 「SHOI2017」分手是祝願

pow sum write inline 正常的 main define loj ios

題目鏈接

LOJ #2145

題解

一道畫風正常的……期望DP?

首先考慮如何以最小步數熄滅所有燈:貪心地從大到小枚舉燈,如果它亮著則修改它。可以求出總的最小步數,設為\(cnt\)

然後開始期望DP。設\(dp[i]\)為當前最小步數是\(i\),總最小步數是\(i\),要達到最小步數是\(i - 1\)的狀態,期望要走多少步。則有\(\frac{i}{n}\)的幾率恰好走了該走的一步,而有\(\frac{n - i}{n}\)的幾率走錯了(回到了\(dp[i + 1]\)表示的狀態)。

則:\[dp[i] = \frac{i}{n} + \frac{n - i}{n}(1 + dp[i + 1] + dp[i])\]

就可以推出來了。

答案就是:\((\sum_{i = k + 1}^{cnt} dp[i] + min(cnt, k)) * n!\)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstdlib>
#define space putchar(' ')
#define enter putchar('\n')
using namespace
std; typedef long long ll; template <class T> void read(T &x){ char c; bool op = 0; while(c = getchar(), c < '0' || c > '9') if(c == '-') op = 1; x = c - '0'; while(c = getchar(), c >= '0' && c <= '9') x = x * 10
+ c - '0'; if(op) x = -x; } template <class T> void write(T x){ if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar('0' + x % 10); } const int N = 100005, P = 100003; int n, m, a[N], cnt; ll dp[N], ans; ll qpow(ll a, ll x){ ll ret = 1; while(x){ if(x & 1) ret = ret * a % P; a = a * a % P; x >>= 1; } return ret; } int main(){ read(n), read(m); for(int i = 1; i <= n; i++) read(a[i]); for(int i = n; i; i--) if(a[i]){ cnt++; for(int j = 1; j * j <= i; j++) if(i % j == 0){ a[j] ^= 1; if(j * j < i) a[i / j] ^= 1; } } for(int i = n; i; i--) dp[i] = 1 + (n - i) * qpow(i, P - 2) % P * (1 + dp[i + 1]) % P; for(int i = cnt; i > m; i--) ans = (ans + dp[i]) % P; ans = (ans + min(cnt, m)) % P; for(int i = 2; i <= n; i++) ans = ans * i % P; write(ans), enter; return 0; }

LOJ #2145. 「SHOI2017」分手是祝願