题意
题解
问题可以转化为每一天与职员之间的匹配问题,思路与 AtCoder ABC320 G Slot Strategy 2 (Hard) 类似。但二分图规模过大,直接求解最大匹配显然难以胜任。
根据 Hall 定理,若二分图一侧点集 S S S 都能被匹配的充要条件是,对于 S S S 的任一子集 A A A,与其存在连边的二分图另一侧节点的数量要大于等于 ∣ A ∣ \vert A\vert ∣A∣。二分答案,上界为 2 ⋅ n ⋅ k 2\cdot n \cdot k 2⋅n⋅k,因为每一个职工的休息日之前总是对应唯一的工作日。预处理出每一天可以选择的用户集合。二分确定工作时长之后,可以统计出对于每一个确定的用户集合 s s s,其恰好能被选择的天数 f ( s ) f(s) f(s)。令 A i A_i Ai 代表用户 i i i 被选择的日子的集合。那么对 f f f 做高维后缀和,就能求出 A i A_i Ai 的交集的规模,为了求出 A A A 的并集的规模,使用容斥原理,求高维前缀和即可。总时间复杂度 O ( n 2 k + n 2 n log ( n k ) ) O\Big(n^2k+n2^n\log(nk)\Big) O(n2k+n2nlog(nk))
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
int lim = 2 * n * k;
vector<int> state(lim);
for (int i = 0; i < lim; ++i) {
int x = 0;
for (int j = 0; j < n; ++j) {
if (i % (2 * a[j]) < a[j]) {
x |= 1 << j;
}
}
state[i] = x;
}
auto judge = [&](int d) {
vector<int> dp(1 << n);
for (int i = 0; i < d; ++i) {
if (state[i] > 0) {
dp[state[i]] += 1;
}
}
for (int i = 0; i < n; ++i) {
for (int j = 1; j < 1 << n; ++j) {
if (~j >> i & 1) {
dp[j] += dp[j ^ 1 << i];
}
}
}
for (int i = 0; i < 1 << n; ++i) {
if (~__builtin_popcount(i) & 1) {
dp[i] *= -1;
}
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < 1 << n; ++j) {
if (j >> i & 1) {
dp[j] += dp[j ^ 1 << i];
}
}
}
for (int i = 1; i < 1 << n; ++i) {
if (dp[i] < __builtin_popcount(i) * k) {
return false;
}
}
return true;
};
int lb = 0, ub = lim;
while (ub - lb > 1) {
int mid = (lb + ub) / 2;
if (judge(mid)) {
ub = mid;
} else {
lb = mid;
}
}
cout << ub << '\n';
return 0;
}