题目
题目链接:https://ac.nowcoder.com/acm/contest/373/D
定义两个位数相等的二进制串 A,B 的相似度 SIM(A,B)= 二进制串A⊕B中0的个数
如 A=00010,B=01000,A⊕B=01010,所以 SIM(A,B)=3。
给定 N 个长度为 M 的二进制串S1,S2...SN。
现在的问题是找出一个额外的长度为 M 的二进制字符串 T ,
使得 max{SIM(S1,T),SIM(S2,T)...SIM(SN,T)} 最小。
因为满足条件的 T 可能不止一个,不需要输出串 T ,只需要输出这个最小值即可。
1<=N<=3e5,1<=M<=20
思路来源
https://www.cnblogs.com/butterflydew/p/10459737.html
https://www.cnblogs.com/LMCC1108/p/10470798.html
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40383289
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40376747
题解
解法1:
多源bfs,一共只有(1<<20)状态,每次只去更新到这个点的最短距离,
那么,我们记录的实际是二进制中A⊕B中1的个数的最小值,
不妨S1和V有3个1,S2和V有5个1,S3和V有7个1,m=20,dis[V]=3
那么S1和V有17个0,S2和V有15个0,S3和V有13个0,m-dis[V]=17
这个1的个数的最小值,被m减去之后,就是0的个数的最大值,
现在令0的个数的最大值这个数最小,也就是令17最小,那么令3最大即可
也就是寻找最大的dis[V],答案ans=m-dis[V]
想明白1和0的关系带来的min和max的转换之后,
寻找max的最小,就是寻找min的最大
正着做不好做,可以向对立做,思维的转化还是很重要呐……
解法2:
利用异或的性质A=B^C的情况下,C=A^B
A数组读入n个数,将读入的数的对应位置赋1,
如果最后的答案次数是x,数是T的话,
那么A内的所有数卷T所得的数,都会落在二进制位为0的个数<=x的数里面
我们只需二分次数x,B数组=[该数的二进制位为0的个数<=x],对应位置赋1
如果A卷B所得的新数组,在某一位置的值为n的话,说明该过程可逆
注意到,A数组的一个数只能对一个答案位置产生1的贡献,所以B数组元素虽然比逆过程多了,但是不影响
相当于,把A数组的每个数看成一个人,相当于第i个人取B区间上的一些点涂颜色i,然后问是否存在点有n种颜色
开始用int+取模,TLE
int不取模存在n==3e5的数据,a[i]*b[i]可能会爆,WA
最后开了ll不取mod,AC
代码1(bfs)
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
queue<int>q;
char s[25];
int dis[1<<20];
int n,m,v;
int tmp,nex,mx;
int to(char s[],int len)
{
int ans=0;
for(int i=0;i<len;++i)
ans=ans*2+(s[i]-'0');
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<(1<<m);++i)
dis[i]=INF;
for(int i=0;i<n;++i)
{
scanf("%s",s);
v=to(s,m);
dis[v]=0;
q.push(v);
}
while(!q.empty())
{
tmp=q.front();q.pop();
for(int i=0;i<m;++i)
{
nex=tmp^(1<<i);
if(dis[nex]>dis[tmp]+1)
{
dis[nex]=dis[tmp]+1;
q.push(nex);
}
}
}
for(int i=0;i<(1<<m);++i)
mx=max(mx,dis[i]);
printf("%d\n",m-mx);
return 0;
}
代码2(fwt)
//inv2是MOD意义下2的逆元,inv2=(MOD+1)/2
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1<<20;
char s[25];
int n,m,N;
int l,r,v;
ll a[MAXN],b[MAXN];
int bit[MAXN];//dp统计每个值有多少位为1
void FWT_xor(ll *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
ll X=a[j+k],Y=a[i+j+k];
a[j+k]=X+Y;a[i+j+k]=X-Y;
if(opt==-1)a[j+k]=a[j+k]/2,a[i+j+k]=a[i+j+k]/2;
}
}
bool ok(int x)
{
for(int i=0;i<N;++i)
b[i]=((m-bit[i])<=x);//二进制中0的个数<=x
FWT_xor(b,1);
for(int i=0;i<N;++i)
b[i]=a[i]*b[i];
FWT_xor(b,-1);
for(int i=0;i<N;++i)
if(b[i]==n)return 1;
return 0;
}
int main()
{
scanf("%d%d",&n,&m);
N=1<<m;
for(int i=0;i<N;++i)//dp统计位数
bit[i]=bit[i>>1]+(i&1);
for(int i=0;i<n;++i)
{
scanf("%s",s);
v=0;
for(int j=0;j<m;++j)
v=v*2+(s[j]-'0');
a[v]++;
}
FWT_xor(a,1);
l=0,r=m;
while(l<r)
{
int mid=(l+r)/2;
if(ok(mid))r=mid;
else l=mid+1;
}
printf("%d\n",l);
return 0;
}