Codeforces 582E

对于一个表达式,我们可以记录 4 4 4个变量的每种不同取值(共 2 4 = 16 2^4=16 24=16种)所对应的不同结果,于是可以把一个表达式看成一个长度为 16 16 16的二进制串,这样的串总共有 2 16 2^{16} 216种。
回到原问题,我们把表达式树建出来,对每个节点 x x x维护子树中的表达式不同填法所对应的每种二进制串 s t st st的个数 F [ x ] [ s t ] F[x][st] F[x][st]。那么考虑 x x x处填的符号为 a n d and and o r or or,分别对应把左右儿子对应的二进制串 a n d and and o r or or起来,于是实质上是做了一个 a n d and and卷积和 o r or or卷积,容易用高维前/后缀和实现。这样也可以方便的统计答案。
时间复杂度 O ( ∣ s ∣ ⋅ 2 16 ⋅ 16 ) \mathcal O(|s|\cdot 2^{16} \cdot 16) O(s21616)

#include <bits/stdc++.h>
#define MOD 1000000007

using namespace std;

typedef long long ll;

inline void add(int &x,int y) {
  ((x+=y)>=MOD)?x-=MOD:0;
}

inline void sub(int &x,int y) {
  ((x-=y)<0)?x+=MOD:0;
}

void fwt1(int *p,int len) {
  for(int i=1;i<len;i<<=1)
    for(int j=0;j<len;j++)
      if (j&i) add(p[j],p[j^i]);
}

void fwt2(int *p,int len) {
  for(int i=1;i<len;i<<=1)
    for(int j=0;j<len;j++)
      if (j&i) add(p[j^i],p[j]);
}

void ifwt1(int *p,int len) {
  for(int i=1;i<len;i<<=1)
    for(int j=0;j<len;j++)
      if (j&i) sub(p[j],p[j^i]);
}

void ifwt2(int *p,int len) {
  for(int i=1;i<len;i<<=1)
    for(int j=0;j<len;j++)
      if (j&i) sub(p[j^i],p[j]);
}

char str[505];

int f[505][1<<16],tot;
int p[1<<16],q[1<<16];

int solve(int l,int r) {
  int o=++tot;
  if (l==r) {
  	if (str[l]=='A'||str[l]=='?') {
  		int st=0;
  		for(int i=0;i<16;i++)
  		  if ((i>>0)&1) st|=(1<<i);
  		f[o][st]=1;
	  }
  	if (str[l]=='B'||str[l]=='?') {
  		int st=0;
  		for(int i=0;i<16;i++)
  		  if ((i>>1)&1) st|=(1<<i);
  		f[o][st]=1;
	  }
  	if (str[l]=='C'||str[l]=='?') {
  		int st=0;
  		for(int i=0;i<16;i++)
  		  if ((i>>2)&1) st|=(1<<i);
  		f[o][st]=1;
	  }
  	if (str[l]=='D'||str[l]=='?') {
  		int st=0;
  		for(int i=0;i<16;i++)
  		  if ((i>>3)&1) st|=(1<<i);
  		f[o][st]=1;
	  }
  	if (str[l]=='a'||str[l]=='?') {
  		int st=0;
  		for(int i=0;i<16;i++)
  		  if (!((i>>0)&1)) st|=(1<<i);
  		f[o][st]=1;
	  }
  	if (str[l]=='b'||str[l]=='?') {
  		int st=0;
  		for(int i=0;i<16;i++)
  		  if (!((i>>1)&1)) st|=(1<<i);
  		f[o][st]=1;
	  }
  	if (str[l]=='c'||str[l]=='?') {
  		int st=0;
  		for(int i=0;i<16;i++)
  		  if (!((i>>2)&1)) st|=(1<<i);
  		f[o][st]=1;
	  }
  	if (str[l]=='d'||str[l]=='?') {
  		int st=0;
  		for(int i=0;i<16;i++)
  		  if (!((i>>3)&1)) st|=(1<<i);
  		f[o][st]=1;
	  }
	return o;
  }
  int ls,rs,mid;
  if (str[l]=='(') {
  	int d=0;
  	for(int i=l;i<=r;i++)
  	  if (str[i]=='(') d++;
  	  else if (str[i]==')') {
  	  	  d--;
  	  	  if (!d) {
  	  	  	    ls=solve(l+1,i-1);
  	  	  	    mid=i+1;
  	  	  	    break;
			  }
		}
  }
  else {
  	ls=solve(l,l);
  	mid=l+1;
  }
  if (str[r]==')') {
  	int d=0;
  	for(int i=r;i>=l;i--)
  	  if (str[i]==')') d++;
  	  else if (str[i]=='(') {
  	  	  d--;
  	  	  if (!d) {
  	  	  	    rs=solve(i+1,r-1);
  	  	  	    break;
			  }
		}
  }
  else rs=solve(r,r);
  if (str[mid]!='&') {
  	memcpy(p,f[ls],sizeof(p));
  	memcpy(q,f[rs],sizeof(q));
  	fwt1(p,1<<16);
  	fwt1(q,1<<16);
  	for(int i=0;i<(1<<16);i++) p[i]=(ll)p[i]*q[i]%MOD;
  	ifwt1(p,1<<16);
  	for(int i=0;i<(1<<16);i++) add(f[o][i],p[i]);
  }
  if (str[mid]!='|') {
  	memcpy(p,f[ls],sizeof(p));
  	memcpy(q,f[rs],sizeof(q));
  	fwt2(p,1<<16);
  	fwt2(q,1<<16);
  	for(int i=0;i<(1<<16);i++) p[i]=(ll)p[i]*q[i]%MOD;
  	ifwt2(p,1<<16);
  	for(int i=0;i<(1<<16);i++) add(f[o][i],p[i]);
  }
  return o;
}

int main() {
  scanf("%s",str+1);
  int n=strlen(str+1);
  solve(1,n);
  int m;
  scanf("%d",&m);
  int st=0,bel=0;
  for(int i=1;i<=m;i++) {
  	int t=0;
  	for(int j=0;j<4;j++) {
  		int x;
  		scanf("%d",&x);
  		if (x) t|=(1<<j);
	  }
	int x;
	scanf("%d",&x);
	if (x) st|=(1<<t);
	bel|=(1<<t);
  }
  int ans=0;
  for(int i=0;i<(1<<16);i++)
    if ((i&bel)==st) add(ans,f[1][i]);
  printf("%d\n",ans); 
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值