1. 程式人生 > >[SHOI 2017] 分手是祝願

[SHOI 2017] 分手是祝願

多個 getchar() push typedef -- 簡單 復雜度 轉移 開關

[題目鏈接]

https://www.lydsy.com/JudgeOnline/problem.php?id=4872

[算法]

首先發現 , 對於一個開關 , 按下2次和沒按是等價的 , 因此每個開關最多按一次

考慮k = n的情況 , 只需簡單倒序貪心即可

考慮隨機的情況 , 由觀察可知一個開關不能由多個開關組合得到

用fi表示i次將所有開關變關到(i - 1)次將所有開關變關的期望步數

有轉移方程fi = i / n + (1 - i / n) * (1 + fi + 1 + fi)

將該轉移方程看作一個一元一次方程 , 即可解出fi

不再贅述 , 詳見代碼

時間復雜度 : O(N)

[代碼]

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int P = 100003;
const int N = 100010;

int n , k , cnt;
int a[N] , inv[N] , dp[N];
vector
< int > D[N]; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if
(c == -) f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - 0; x *= f; } int main() { read(n); read(k); for (int i = 1; i <= n; ++i) read(a[i]); inv[1] = 1; for (int i = 2; i <= n; ++i) inv[i] = 1ll * (P - P / i) * inv[P % i] % P; for (int i = 1; i <= n; ++i) { for (int j = i; j <= n; j += i) { D[j].push_back(i); } } for (int i = n; i >= 1; --i) { if (a[i]) { for (unsigned j = 0; j < D[i].size(); ++j) a[D[i][j]] ^= true; ++cnt; } } int ans = 0; if (cnt <= k) ans = cnt; else { dp[n] = 1; for (int i = n - 1; i >= 1; --i) dp[i] = (1ll * dp[i + 1] * (n - i) % P * inv[i] % P + 1ll * n * inv[i] % P) % P; for (int i = cnt; i > k; --i) ans = (ans + dp[i]) % P; ans = (ans + k) % P; } for (int i = 1; i <= n; ++i) ans = 1ll * ans * i % P; printf("%d\n" , ans); return 0; }

[SHOI 2017] 分手是祝願