正题
前几天才玩过容斥的我在考场上还是一如既往的蠢。
首先显然。
对于的情况,我们预处理子集后缀和,每输入一个串,把,然后对于原本的0进行容斥。
对于的情况,我们预处理子集前缀和,每输入一个串,把,然后对于原本的1进行容斥。
至于怎么容斥,可以先做做其他子集容斥的题,容斥系数可以用二项式定理简单证明。
对于的情况,暴力枚举即可。
#include<bits/stdc++.h>
using namespace std;
const int N=20;
int n,q,m,ans;
int f[1<<N],g[1<<N],p[1<<N];
int coef[1<<N];
char s[1<<N];
int main(){
scanf("%d %d",&n,&q);m=1<<n;
scanf("%s",s);
for(int i=0;i<m;i++) g[i]=p[i]=f[i]=s[i]-'0';
for(int l=2;l<=m;l<<=1)
for(int i=0;i<m;i+=l)
for(int x=i,y=i+l/2;y<i+l;x++,y++)
f[y]+=f[x],g[x]+=g[y];
coef[0]=1;for(int i=1;i<m;i++) coef[i]=-coef[i&(i-1)];
while(q--){
scanf("%s",s);
int t1=0,t2=0,t3=0,a=0,b=0;
for(int i=0;i<n;i++)
if(s[i]=='0') t1++;
else if(s[i]=='1') t2++;
else t3++;
ans=0;
if(t1<=t2 && t1<=t3){
for(int i=0;i<n;i++){
a*=2,b*=2;
if(s[i]=='0') b++;
else if(s[i]=='1') a++,b++;
}
for(int j=b-a;;j=(j-1)&(b-a)){
ans+=coef[j]*g[a+j];
if(j==0) break;
}
}
else if(t2<=t1 && t2<=t3){
for(int i=0;i<n;i++){
a*=2,b*=2;
if(s[i]=='1') b++;
else if(s[i]=='?') a++,b++;
}
for(int j=b-a;;j=(j-1)&(b-a)){
ans+=coef[j]*f[b-j];
if(j==0) break;
}
}
else{
for(int i=0;i<n;i++){
a*=2,b*=2;
if(s[i]=='?') b++;
else if(s[i]=='1') a++,b++;
}
for(int j=b-a;;j=(j-1)&(b-a)){
ans+=p[a+j];
if(j==0) break;
}
}
printf("%d\n",ans);
}
}