bitmask集合

SOS dp+randomization

D. Love-Hate
题意:找一个最大的硬币集合,满足:n(1e5)个中至少n/2个人有全部这些硬币。(答案大小不超过15)
思路:
看到n/2:考虑randomization。随机选30个不重复的人,都不属于某个大小为n/2的集合的概率为1/{2^30}。
于是我们可以随机选一个人。认为这个人在最优解中。
那么最大的硬币集合肯定是这个人喜欢的硬币的子集。
我们把这个人喜欢的硬币离散化(设为sz)。
然后遍历所有sz的子集。找到{满足【n/2个人都有这子集】的集合中}大小最大的那一个。反离散化就是答案。
发现如果遍历子集,再遍历人,会有 30 × 2 15 × 1 e 5 30\times 2^{15}\times 1e5 30×215×1e5这么大,会TLE。
考虑如何优化。有一个算法叫做SOS,这个算法是用来计算: ∀ x , s u m [ x ] = ∑ y ⊆ x a [ y ] {\forall x},sum[x]=\sum_{y\subseteq x} a[y] x,sum[x]=yxa[y]的。

//memory optimized, super easy to code.
for(int i = 0; i<(1<<N); ++i)
	F[i] = A[i];
for(int i = 0;i < N; ++i) for(int mask = 0; mask < (1<<N); ++mask){
	if(mask & (1<<i))
		F[mask] += F[mask^(1<<i)];
}

时间复杂度为 N 2 N N2^N N2N
我们现在考虑的是,对于每个子集,统计有这个子集的人的数目。我们先得到该人的集合,该集合的人++。然后再讲该集合的子集全部++。那么就是对每一个集合,遍历其子集的问题了。只不过这一次更新的是子集。
反过来就可以了。

for(int i=0;i<sz;++i)
    for(int j=0;j<(1<<sz);++j){
        if(j&(1<<i)) cnt[j^(1<<i)] += cnt[j];
    }

总时间复杂度为 i t e r ( 2 p p + n ) iter(2^p p+n) iter(2pp+n) 30 × ( 2 15 × 15 + 1 e 15 ) 30\times (2^{15}\times 15+1e15) 30×(215×15+1e15)

#include<bits/stdc++.h>

using namespace std;
#define mp(a, b) make_pair(a,b)
#define vi vector<int>
#define mii map<int,int>
#define mpi map<pair<int,int>,int>
#define vp vector<pair<int,int> >
#define pb(a) push_back(a)
#define fr(i, n) for(i=0;i<n;i++)
#define rep(i, a, n) for(i=a;i<n;i++)
#define F first
#define S second
#define endl "\n"
#define Endl "\n"
#define fast std::ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define mod 1000000007
#define dom 998244353
#define sl(a) (int)a.length()
#define sz(a) (int)a.size()
#define all(a) a.begin(),a.end()
#define pii pair<int,int>
#define mini 2000000000000000000
#define time_taken 1.0 * clock() / CLOCKS_PER_SEC

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
//const long double pi = acos(-1);
//mt19937_64 mt(chrono::steady_clock::now().time_since_epoch().count());
//primes for hashing 937, 1013
template<typename T, typename U>
static inline void amin(T &x, U y) {
    if (y < x)
        x = y;
}

template<typename T, typename U>
static inline void amax(T &x, U y) {
    if (x < y)
        x = y;
}

#define ll long long
int main() {
    fast;
    int n,m,p;cin>>n>>m>>p;
    vector<ll> a(n);
    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> order(n);
    iota(order.begin(),order.end(),0);
    shuffle(order.begin(),order.end(),rng);
    int best = 0;
    string ans(m,'0');
    for(int it=0;it<min(n,30);++it){
        int me=order[it];
        vector<int>bits; // 存位置
        for(int i=0;i<m;++i){
            if((a[me]>>i)&1)
                bits.pb(i);
        }
        int sz = (int)bits.size();
        vector<int> cnt(1<<sz);
        for(int i=0;i<n;++i){
            int u=0;
            for(int j=0;j<sz;++j){
                if((a[i]>>bits[j])&1) u|=(1<<j);
            }
            cnt[u]++;
        }
        for(int i=0;i<sz;++i)
            for(int j=0;j<(1<<sz);++j){
                if(j&(1<<i)) cnt[j^(1<<i)] += cnt[j];
            }
        for(int i=0;i<(1<<sz);++i){
            // 找答案
            if(2*cnt[i] >= n && __builtin_popcount(i) > best){
                best = __builtin_popcount(i);
                ans = string(m,'0');
                for(int j=0;j<sz;++j){
                    if(i&(1<<j)) ans[bits[j]] = '1';
                }
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值