传送门

给定 n n n个长 m m m位的二进制串

求一个二进制串 T T T,定义 v a l i val_i vali T T T与第 i i i个二进制串相同的位数

使得 m a x ( v a l 1 , v a l 2 . . . . v a l n ) max(val_1,val_2....val_n) max(val1,val2....valn)最小,只需要输出这个最小值。


因为总的状态只有 2 20 2^{20} 220,要是能求出每个状态到 n n n个串的 m a x max max问题就迎刃而解了

如果把这 n n n个二进制串的任何一个作为 T T T,答案是 m m m,因为和自己所有位是相同的

而且改变自己任何一位二进制作为 T T T, v a l val val值就是 m − 1 m-1 m1

但是结合只有最大的 v a l val val有用,我们想到了多起点 b f s bfs bfs

把这 n n n个串拿去 b f s bfs bfs,谁先到状态 k k k,对 k k k而言谁就是最大的 m a x max max

这样每个状态入队一次,可以在优秀的复杂度内通过此题

#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <stdio.h>
using namespace std;
const int maxn = 3e5+10;
queue<int>q;
const int inf = 1e9;
int f[1<<21],n,m;
char s[maxn][25];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)   scanf("%s",s[i] );
    for(int i=0;i<=(1<<20);i++) f[i] = inf;
    for(int i=1;i<=n;i++)
    {
        int sta = 0;
        for(int j=0;j<m;j++)    if( s[i][j]=='1' )  sta|=(1<<j);
        f[sta] = m;
        q.push( sta );
    }
    //让最大相似度最小
    int ans = inf;
    while( !q.empty() )
    {
        int u = q.front(); q.pop();
        ans = min( ans,f[u] );
        for(int i=0;i<m;i++)
        {
            int temp = ((1<<i)^u);
            if( f[temp]==inf )
            {
                f[temp] = f[u]-1;
                q.push( temp );
            }
        }
    }
    printf("%d",ans);
}

还有一种神仙做法二分 + F W T +FWT +FWT

首先我们把读入的串 x x x a [ x ] = 1 a[x]=1 a[x]=1

然后假如知道了串 T T T,令 b [ T ] = 1 b[T]=1 b[T]=1,设答案为 a n s ans ans

这样 a a a b b b做异或卷积得到 c c c

c i = ∑ j ⊕ k = i b j ∗ a k c_i=\sum\limits_{j\oplus k=i}b_j*a_k ci=jk=ibjak,有值的 c i c_i ci满足 i i i的二进制 0 0 0不超过 a n s ans ans

现在我们考虑逆过程,二分这个 a n s ans ans

那么给 c c c数组赋值,所有二进制不大于 a n s ans ans 0 0 0的下标赋值为 1 1 1

a a a c c c做异或卷积可以得到数组 b b b

这个数组 b b b的任意满足 b [ i ] = n b[i]=n b[i]=n就是可行的,因为对于 b i b_i bi来说, a a a的每个元素只能造成一点贡献

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1<<23;
#define int long long
int a[maxn],b[maxn],bit[maxn],n,m,mx;
void XOR(int f[],int type)
{
	for(int mid=2,k=1;mid<=mx;mid<<=1,k<<=1)
	for(int i=0;i<mx;i+=mid)
	for(int j=0;j<k;j++)
	{
		int t = i+j, v = i+j+k;
		f[t] = f[t]+f[v];
		f[v] = f[t]-f[v]-f[v];
		if( type==-1 )
			f[t]=f[t]/2, f[v]=f[v]/2;
	}
}
bool isok(int x)
{
	for(int i=0;i<mx;i++)
		b[i] = ( (m-bit[i])<=x );//二进制0个数<=x
	XOR(b,1);
	for(int i=0;i<mx;i++)	b[i] = a[i]*b[i];
	XOR(b,-1); 
	for(int i=0;i<mx;i++)	
		if( b[i]==n )	return true;
	return false;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	mx = (1<<m);
	for(int i=0;i<mx;i++)	bit[i] = bit[i>>1]+(i&1);
	for(int i=1;i<=n;i++)
	{
		int now = 0; string s; cin >> s;
		for(int j=0;j<m;j++)
			if( s[j]=='1' )	now |= (1<<j);
		a[now]++;
	}
	XOR(a,1);
	int l = 0,r = m,ans=m;
	while( r>=l )
	{
		int mid = l+r>>1;
		if( isok(mid) )	r = mid-1,ans=mid;
		else	l = mid+1;
	}
	printf("%lld",ans);
}