思路:
如果知道答案是某个人的子集以及这个人的编号,那么能不能算出答案,以及时间复杂度是多少,然后考虑到 生日悖论 ,因为答案是 个人的子集,所以我们任意选一个人 ,答案不是 的子集的概率是 (实际更小),那么我们只要取 30 个人,这些人的子集都不包含答案的概率就小到,几乎可以认为不可能发生。

状压每个子集有多少人喜欢,这些子集其实是和不喜欢的货币是没关系的,所以可以离散化,所有喜欢的货币,然后把它们离散化为表示第个人和喜欢的货币的最大交集(状压),先存下,然后通过一种货币来状态转移,比如:
代码中状态转移的两层循环不能对调位置,一定要先枚举某一位,然后再枚举每个状态,这样才能保证后面的状态不被重复计算。

mt19937 read(time(0));时不能关流。注意所有人喜欢的货币集合小于30的情况,防止死循环。

MyCode:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e4 + 7, maxm = 2e5 + 7;
mt19937 read(time(0));
int f[maxn],cnt[maxn],mx;
string ans,s[maxm];
map<string,bool>vis;
set<string>ft;
vector<int>pos;
int main() {
//    ios::sync_with_stdio(false);
//    cin.tie(0),cout.tie(0);//
    int n,m,p;
    cin>>n>>m>>p;
    for(int i=1;i<=n;++i) cin>>s[i],ft.insert(s[i]);
    string e(m,'0');
    ans=e;
    int end=1<<p;
    for(int i=0;i<end;++i) cnt[i]=cnt[i-(i&-i)]+1;
    uniform_int_distribution<int>lim(1,n);
    for(int cas=min((int)ft.size(),30);cas;--cas) {
        int x=lim(read);
        while(vis[s[x]]) x=lim(read);
        vis[s[x]]=true;
        pos.clear();
        for(int i=0;i<m;++i)
            if(s[x][i]=='1') pos.emplace_back(i);
        memset(f,0,sizeof f);
        for(int i=1;i<=n;++i) {
            int tmp(0);
            for(int j:pos) {
                tmp<<=1;
                if(s[i][j]=='1') tmp|=1;
            }
            ++f[tmp];
        }
        end=1<<pos.size();
        for(int i=0;i<pos.size();++i)
            for(int j=1;j<end;++j) 
                if(j&(1<<i)) f[j^(1<<i)]+=f[j];
        for(int i=1;i<end;++i) if(f[i]*2>=n&&cnt[i]>cnt[mx]) {
            mx=i;
            ans=e;
            for(int j=pos.size()-1,k=i;~j;--j,k>>=1) if(k&1) 
                ans[pos[j]]='1';
        }
    }
    cout<<ans<<'\n';
    return 0;
}