一、題目
二、解法
考慮一個子集反演:
下面給出證明:
對於本題,因爲,我們分情況討論:
- 最小值是時,直接暴力枚舉。
- 最小值是時,就用子集反演,時間複雜度
- 最小值是時,就把變成,變成,初始數組還是一樣,還是不變,就用的方法。
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 1<<20;
int read()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,m,lim,a[M],b[M],c[M],bit[M];char s[M];
void fwt(int *a,int n,int op)
{
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++)
{
if(op==1) a[i+j+k]=a[i+j+k]+a[j+k];
else a[i+j+k]=a[i+j+k]-a[j+k];
}
}
signed main()
{
n=read();m=read();
scanf("%s",s);lim=1<<n;
for(int i=0;i<lim;i++)
{
a[i]=b[(lim-1)^i]=c[i]=s[i]-'0';
bit[i]=bit[i>>1]+(i&1);
}
fwt(a,lim,1);fwt(b,lim,1);
while(m--)
{
scanf("%s",s);
int c0=0,c1=0,cw=0,s0=0,s1=0,sw=0,ans=0;
for(int i=0;i<n;i++)
{
if(s[i]=='0') c0++,s0|=(1<<(n-i-1));
if(s[i]=='1') c1++,s1|=(1<<(n-i-1));
if(s[i]=='?') cw++,sw|=(1<<(n-i-1));
}
int mn=min(c0,min(c1,cw));
if(mn==cw)
{
for(int i=sw;;i=(i-1)&sw)
{
ans+=c[i^s1];
if(i==0) break;
}
}
else if(mn==c1)
{
for(int i=s1;;i=(i-1)&s1)
{
if(bit[s1^i]&1) ans-=a[i|sw];
else ans+=a[i|sw];
if(i==0) break;
}
}
else if(mn==c0)
{
for(int i=s0;;i=(i-1)&s0)
{
if(bit[s0^i]&1) ans-=b[i|sw];
else ans+=b[i|sw];
if(i==0) break;
}
}
printf("%d\n",ans);
}
}