1523 D. Love-Hate(随机+枚举子集或SOSDP)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1523 D. Love-Hate(随机+枚举子集或SOSDP)相关的知识,希望对你有一定的参考价值。
设答案子集为x,注意到有超过 n 2 \\frac{n}{2} 2n(四舍五入)的人包括 x x x
我们从 n n n个人随机选出一个人,这个人包含 x x x的概率大于等于 n 2 \\frac{n}{2} 2n
多随机几次,意味着肯定随机到了参与答案的人。
比如随机到的人编号是 i d id id,那么只考虑这个人喜欢的货币子集,设这个人喜欢 l l l种货币
于是把所有人抽象为只包含这 l l l位的二进制数,这样的子集只有不多于 2 l 2^{l} 2l个
下面有两种做法
①.开桶枚举子集
开个桶,把每个人表示的数字存下来,此时数组 w [ m a s k ] w[mask] w[mask]表示二进制数为 m a s k mask mask有 w [ m a s k ] w[mask] w[mask]个人
接下来令 x x x从 0 0 0枚举到 2 l 2^l 2l,然后枚举 x x x的子集,让 x x x的子集 m a s k mask mask, v i s [ m a s k ] + = w [ x ] vis[mask]+=w[x] vis[mask]+=w[x]
最后扫描一遍 v i s vis vis数组,就可以轻松判断是否大于等于 n 2 \\frac{n}{2} 2n然后更新答案
②. S O S D P \\rm SOSDP SOSDP
现在我们只需要预处理一个 f [ m a s k ] f[mask] f[mask]表示子集是 m a s k mask mask的有多少人
这其实是个非常模板的 S O S D P \\rm SOSDP SOSDP问题,套用模板即可
总体复杂度 O ( k ∗ p ∗ 2 p ) O(k*p*2^{p}) O(k∗p∗2p)
其中 k k k表示随机次数
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
char a[maxn][65],res[maxn];
int zhi[maxn],pos[70],bit[1<<16],f[1<<16],ans=-1,n,m,p;
mt19937 rnd(time(0));
void update(int id)
{
pos[0] = 0;
for(int i=1;i<=n;i++) zhi[i] = 0;
for(int i=1;i<=m;i++) if( a[id][i]=='1' ) pos[++pos[0]] = i;
for(int i=1;i<=n;i++)
for(int j=pos[0];j>=1;j--)
zhi[i] = zhi[i]*2+( a[i][pos[j]]=='1' );
int mx = ( 1<<pos[0] );
for(int i=0;i<mx;i++) f[i] = 0;
for(int i=1;i<=n;i++) f[zhi[i]]++;
for(int i=0;i<pos[0];i++)
for(int j=0;j<mx;j++)
if( !(j&(1<<i)) ) f[j] += f[j|(1<<i)];
for(int i=0;i<mx;i++)
{
if( f[i]<(int)(n/2.0+0.5) || ans>=bit[i] ) continue;
ans = bit[i];
for(int j=1;j<=m;j++) res[j] = '0';
for(int j=0;j<pos[0];j++)
if( (i>>j)&1 ) res[pos[j+1]] = '1';
}
}
int main()
{
for(int i=1;i<(1<<16);i++) bit[i] = bit[i>>1]+(i&1);
cin >> n >> m >> p;
for(int i=1;i<=n;i++) scanf("%s",a[i]+1 );
for(int i=1;i<=10;i++)
{
int id = rnd()%n+1;
update( id );
}
for(int i=1;i<=m;i++) cout << res[i];
}
以上是关于1523 D. Love-Hate(随机+枚举子集或SOSDP)的主要内容,如果未能解决你的问题,请参考以下文章
1523 E - Crypto Lights(期望+组合数学思维)