[SHOI 2017] 分手是祝愿
[题目链接]
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; }