Codeforces Round #323 (Div. 1) E

题意:

给定函数f(A,B,C,D),其中A,B,C,D∈{0,1),然后a = A^1,b = B^1,c = C^1,d = D^1

题目给出函数的表达式,,很多很多的(x) op (y)形式,()内为变量或嵌套表达式,op为逻辑运算符&或|

其中,(?)代表这个变量由你决定,若op == '?',那么这个表达式由你决定

下面给出m条限制,每条为f(ai,bi,ci,di) = ei的形式

问在给定限制下,有多少可行的方案,使得将f(A,B,C,D)补全后满足所有限制???


solution:

这根本想不出来啊。。。。。。。。。

首先建立出一棵表达式树,方法是每次选择当前剩余表达式中最后一个运算符为树根,然后分成左右两半

最后,,,叶子节点为变量

A,B,C,D的取值情况一共有2^4种,每种情况f(A,B,C,D)都有一个值

定义f[i][mask]为以i为根的子树中的方案数

其中,,,对于mask

不是一共有2^4种ABCD的取值嘛,,这就变成一个2^4位的二进制数码,其中每一位的值对应A,B,C,D选取当前方案时,f(A,B,C,D)的值,此时的方案数(这句话题解里相当难翻译。。。。日)

对于叶子节点,暴力地枚举mask,然后暴力地判断当前点填A,B,C,D,a,b,c,d中哪些能够满足全部16个限制

如果当前节点在表达式中已经确定,暴力判断能否满足即可

对于非叶子节点,逻辑运算符无非是&|两种

f[maski&maskj] += f[lc][maski]*f[rc][maskj]

这是对于&运算的,显然转移方程可以这样写

暴力转移的话,每个mask要枚举其所有子集然后瞎搞,大概要O(2^32)

不过,转移是逻辑运算卷积的形式,,,上FWT即可。。。

其实本题题意和题解还是要靠自己理解的。。。。语言无法表达清楚

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
#include<cmath>  
using namespace std;  
  
const int maxn = 555;  
const int T = 16;  
const int maxm = 1<<T;  
const string S = "ABCDabcd?";  
const int mo = 1000000007;  
  
int ans,f[maxn][maxm],g[maxm];  
int n,m,cnt = 1,typ[maxn],lc[maxn],rc[maxn],Mark[maxm],va[8],ti[8];  
char s[maxn];  
  
void Build(int now,int l,int r)  
{  
	if (l == r) {
    	for (int i = 0; i < 9; i++)  
        	if (S[i] == s[l]) {typ[now] = i; return;}  
	}
	int p = 0,k = -1;
	for (int i = l; i <= r; i++) {
		if (s[i] == '(') {++p; continue;}
		if (s[i] == ')') {--p; continue;}
		if (p) continue;
		k = i; break;
	}
	if (k == -1) {Build(now,l+1,r-1); return;}
	lc[now] = ++cnt; Build(cnt,l,k-1);
	rc[now] = ++cnt; Build(cnt,k+1,r);
	if (s[k] == '&') typ[now] = 9;
	else if (s[k] == '|') typ[now] = 10;
	else typ[now] = 11;
}  
  
void fwt_and(int *A)  
{  
    for (int i = 0; i < T; i++)  
        for (int j = 0; j < maxm; j++) {  
            if (j&(1<<i)) continue;  
            int u = A[j],v = A[j|(1<<i)];  
            A[j] = (u + v) % mo; A[j|(1<<i)] = v;  
        }  
}  
  
void ifwt_and(int *A)  
{  
    for (int i = 0; i < T; i++)  
        for (int j = 0; j < maxm; j++) {  
            if (j&(1<<i)) continue;  
            int u = A[j],v = A[j|(1<<i)];  
            A[j] = (u - v + mo) % mo; A[j|(1<<i)] = v;  
        }  
}  
  
void fwt_or(int *A)  
{  
    for (int i = 0; i < T; i++)  
        for (int j = 0; j < maxm; j++) {  
            if (j&(1<<i)) continue;  
            int u = A[j],v = A[j|(1<<i)];  
            A[j] = u; A[j|(1<<i)] = (v + u) % mo;  
        }  
}  
  
void ifwt_or(int *A)  
{  
    for (int i = 0; i < T; i++)  
        for (int j = 0; j < maxm; j++) {  
            if (j&(1<<i)) continue;  
            int u = A[j],v = A[j|(1<<i)];  
            A[j] = u; A[j|(1<<i)] = (v - u + mo) % mo;  
        }  
}  
  
void Work(int now)  
{  
    if (typ[now] < 9) {  
        for (int i = 0; i < maxm; i++) {  
            memset(ti,0,sizeof(ti));
            for (int j = 0; j < 16; j++) {  
                for (int k = 0; k < 4; k++) 
					va[k] = (j>>k)&1,va[k+4] = va[k]^1;  
                int E = (i>>j)&1;  
                if (typ[now] < 8) {  
                    if (va[typ[now]] == E) ++ti[typ[now]];
                    continue;  
                }  
                for (int k = 0; k < 8; k++) 
					if (va[k] == E) ++ti[k];  
            }  
            int tot = 0;
            for (int I = 0; I < 8; I++)
            	if (ti[I] == 16) ++tot;
            f[now][i] = tot;  
        }  
        return;  
    }  
    Work(lc[now]); Work(rc[now]);
	if (typ[now] == 9) {
    	fwt_and(f[lc[now]]);  
    	fwt_and(f[rc[now]]);  
    	for (int i = 0; i < maxm; i++)  
    	    f[now][i] = 1LL*f[lc[now]][i]*f[rc[now]][i]%mo;  
    	ifwt_and(f[now]);  
	}
	else if (typ[now] == 10) {
    	fwt_or(f[lc[now]]);  
    	fwt_or(f[rc[now]]);  
    	for (int i = 0; i < maxm; i++)  
    	    f[now][i] = 1LL*f[lc[now]][i]*f[rc[now]][i]%mo;  
    	ifwt_or(f[now]);  
	}
	else {
    	fwt_and(f[lc[now]]);  
    	fwt_and(f[rc[now]]);  
    	for (int i = 0; i < maxm; i++)  
    	    f[now][i] = 1LL*f[lc[now]][i]*f[rc[now]][i]%mo;  
    	ifwt_and(f[now]);  
    	ifwt_and(f[lc[now]]);  
    	ifwt_and(f[rc[now]]);  
    	fwt_or(f[lc[now]]);  
    	fwt_or(f[rc[now]]);  
    	memset(g,0,sizeof(g));  
    	for (int i = 0; i < maxm; i++)  
    	    g[i] = 1LL*f[lc[now]][i]*f[rc[now]][i]%mo;  
    	ifwt_or(g);  
    	for (int i = 0; i < maxm; i++)  
    	    f[now][i] = (f[now][i] + g[i]) % mo;  	
	}
}  

bool Judge(int x)
{           
	for (int j = 0; j < 16; j++) {
		if (Mark[j] == -1) continue;
    	for (int k = 0; k < 4; k++) 
			va[k] = (j>>k)&1,va[k+4] = va[k]^1;  
        int E = (x>>j)&1;  
		if (Mark[j] != E) return 0;
	}
	return 1;
}

int main()  
{  
    #ifdef DMC  
        freopen("DMC.txt","r",stdin);  
    #endif  
      
    scanf("%s",s + 1);  
    n = strlen(s + 1);  
    memset(Mark,-1,sizeof(Mark));  
    cin >> m;  
    while (m--) {  
        int A,B,C,D,E;  
        scanf("%d%d%d%d%d",&A,&B,&C,&D,&E);  
        int G = A + (B<<1) + (C<<2) + (D<<3);  
        Mark[G] = E;  
    }  
    Build(1,1,n);  
    Work(1);  
    for (int i = 0; i < maxm; i++) 
    	if (Judge(i))
			ans = (ans + f[1][i]) % mo;
    cout << ans;
    return 0;  
}  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值