限制问题

问题描述

\(n(n \le 20)\) 个数 \(0 \sim n - 1\),现在要选出若干个数。

给定 \(m \le 2 \times 10^5\) 条限制,每条限制给定集合 \(S \subseteq \{0, 1, 2, \dots n - 1\}\),要求选出的数中至少有一个 \(x \in S\)。问至少需要选出几个数。

解决方式

令全集 \(U = \{0, 1, 2, \dots, n - 1 \}\)

考虑每种选取方式。对于每条限制,直接做比较复杂,考虑反面:令 \(P = \complement_U S\),那么所有 \(P\) 的子集都不满足要求。在 \(P\) 位置打个标记,最后做高维前缀和即可。

时间复杂度:\(O(nm + n2^n)\)

#include <bits/stdc++.h>

using namespace std;

const int MAXU = (1 << 20);

int n, m, f[MAXU];

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m;
  int U = (1 << n) - 1; //全集
  for (int i = 1, k; i <= m; i++) {
    cin >> k;
    int u = 0;
    for (int x; k--; ) {
      cin >> x, u ^= (1 << x);
    }
    f[U ^ u]++; // 打标记
  }
  for (int i = 0; i < n; i++) { // 高维前缀和
    for (int j = U; j >= 0; j--) {
      if (((j >> i) & 1) ^ 1) {
        f[j] += f[j ^ (1 << i)];
      }
    }
  }
  int ans = n;
  for (int i = 0; i <= U; i++) {
    if (f[i]) continue;
    int c = 0;
    for (int x = 0; x < n; x++) {
      c += ((i >> x) & 1);
    }
    ans = min(ans, c);
  }
  cout << ans;
  return 0;
}

现在你应该已经会了吧,来试试 CF1995D

posted @ 2025-08-25 13:44  xiehanrui0817  阅读(14)  评论(0)    收藏  举报