https://codeforces.ml/contest/1523/problem/D
题意:
现在有\(m\)种货币,每个人最多会喜欢\(p\)种货币。现在要选择最多的货币,使得不少于一半的人都喜欢这些货币,输出其中的一种方案。
思路:
要不少于一半,很容易想到我们随机去取一个人,就有\(\frac{1}{2}\)的概率能取到在最终集合中的人。\(m\)太大了,但是\(p \leq 15\),选取一个人的时候,把他喜欢的货币序号给取出来,就能用二进制来表示了。设我们选取的人为\(i\),我们枚举\(j\),假设j和i都属于最终的集合,把他们最大相同的货币情况取出来,转换为10进制,用数组存下来。然后,我们再考虑其中不选取某个货币的情况。注意枚举货币的状态要从小到大枚举,避免重复计数。这样子就得到了选取第\(i\)的人的情况下,所有货币选取情况的可参与人数了,维护答案的最大值就行了。
关于rng随机数,贴个链接mark一下。
https://codeforces.com/blog/entry/61587
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 7;
int n, m, p;
ll a[N];
void solve() {
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
cin >> n >> m >> p;
for (int i = 0; i < n; ++i) {
string s;
cin >> s;
for (int j = 0; j < m; ++j) {
if (s[j] == '1') a[i] |= 1ll << j;
}
}
vector<int> G(n);
iota(G.begin(), G.end(), 0);
shuffle(G.begin(), G.end(), rng);
string ans(m, '0');
int res = 0;
for (int i = 0; i < min(n, 100); ++i) {
int v = G[i];
vector<int> V;
for (int j = 0; j < m; ++j) {
if ((a[v] >> j) & 1) V.push_back(j);
}
int sz = V.size();
vector<int> nums(1 << sz);
for (int j = 0; j < n; ++j) {
int u = 0;
for (int k = 0; k < sz; ++k) {
if ((1ll << V[k]) & a[j]) u |= 1 << k;
}
nums[u]++;
}
for (int j = 0; j < sz; ++j) {
for (int k = 0; k < (1 << sz); ++k) {
if (k & (1 << j)) {
nums[k ^ (1 << j)] += nums[k];
}
}
}
for (int j = 0; j < (1 << sz); ++j) {
if (nums[j] * 2 >= n) {
if (__builtin_popcount(j) > res) {
res = __builtin_popcount(j);
ans = string(m, '0');
for (int k = 0; k < sz; ++k) {
if (j & (1 << k)) ans[ V[k] ] = '1';
}
}
}
}
}
cout << ans << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
cout << fixed << setprecision(20);
int t = 1;
//cin >> t;
// t = rd();
while (t--) solve();
return 0;
}