[JOI 2018 Final]毒蛇越狱

Posted by Dispwnl on April 17, 2019

题目

题解

考虑暴力,用$N_c$表示字符$c$在询问字符串中出现的次数

  • 可以$O(2^{N_?})$枚举$?$选什么
  • 可以处理出来每个状态的超集的毒性和,然后可以$O(N_02^{N_0})$处理出来$F_i$,表示至少有$i$个$0$的毒性和,容斥即可
  • 可以处理出来每个状态的子集的毒性和,然后可以$O(N_12^{N_1})$处理出来$G_i$,表示至多有$i$个$1$的毒性和,容斥即可

发现$\min(N_0,N_1,N_?)\le 6$,每次取最小的处理即可

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
using namespace std;
const int MAX=(1<<20);
int L,Q,lim,num0,num1,num_;
int f[MAX],g[MAX],A[20],B[20],C[20],F[64];
char a[MAX],b[20];
int main()
{
	scanf("%d%d%s",&L,&Q,a),lim=1<<L;
	for(int i=0;i<lim;++i)
	  f[i]=g[i]=a[i]-'0';
	for(int j=0;j<L;++j)
	  for(int i=0;i<lim;++i)
	    if(i&(1<<j)) f[i-(1<<j)]+=f[i];
	for(int j=0;j<L;++j)
	  for(int i=0;i<lim;++i)
	    if(i&(1<<j)) g[i]+=g[i-(1<<j)];
	for(int q=1,ans,T,T1;q<=Q;++q)
	  {
	  	scanf("%s",b),num0=num1=num_=T=T1=ans=0;
	  	for(int i=0;i<L;++i)
	  	  if(b[i]=='0') A[num0++]=i;
	  	  else if(b[i]=='1') B[num1++]=i,T+=1<<(L-i-1);
	  	  else if(b[i]=='?') C[num_++]=i,T1+=1<<(L-i-1);
	  	if(num_<=6)
	  	{
	  		for(int i=0,x;i<(1<<num_);++i)
	  		  {
	  		  	x=T;
	  		  	for(int j=0;j<num_;++j)
	  		  	  if(i&(1<<j)) x+=1<<(L-C[j]-1);
	  		  	ans+=a[x]-'0';
			  }
			printf("%d\n",ans);
		}
		else if(num0<=6)
		{
			for(int i=0;i<=num0;++i)
			  F[i]=0;
			for(int i=0,x,num;i<(1<<num0);++i)
			  {
			  	x=T,num=0;
			  	for(int j=0;j<num0;++j)
			  	  if(!(i&(1<<j))) x+=1<<(L-A[j]-1);
			  	  else ++num;
			  	F[num]+=f[x];
			  }
			for(int i=num0,tt=1;i>=0;--i)
			  ans+=tt*F[i],tt*=-1;
			printf("%d\n",ans);
		}
		else if(num1<=6)
		{
			for(int i=0;i<=num1;++i)
			  F[i]=0;
			for(int i=0,x,num;i<(1<<num1);++i)
			  {
			  	x=T1,num=0;
			  	for(int j=0;j<num1;++j)
			  	  if(!(i&(1<<j))) x+=1<<(L-B[j]-1);
			  	  else ++num;
			  	F[num]+=g[x];
			  }
			for(int i=0,tt=1;i<=num1;++i)
			  ans+=tt*F[i],tt*=-1;
			printf("%d\n",ans);
		}
	  }
	return 0;
}