【校内模拟】命题演算(FWT)(中缀表达式转后缀表达式)(模拟)

简要题意:

你有两种可用的逻辑符号,分别是 → \rightarrow ¬ \neg ¬

现在有 k k k 个独立的命题变元,和一个未知的公式 Q Q Q,你只知道 Q Q Q 当中总共个存在恰好 n n n 个上述逻辑符号,且仅会存在上述 k k k 个命题变元。

m m m 个询问。每次给出一个公式 P P P,且 P P P 的若干部分可能被 Q Q Q 取代。

询问有多少种可能的 Q Q Q 使得 P P P 永真。

k ≤ 4 , n ≤ 70 , m ≤ 500 k\leq 4,n\leq 70,m\leq 500 k4,n70,m500


题解:

本来还以为要转成析取范式或者合取范式来搞,仔细一想发现不可实现。

发现 k k k 只有 4 4 4,也就是说 2 k = 16 2^k=16 2k=16 中命题变元取值情况可以暴力枚举,然后看允许 Q Q Q 取哪些情况。

发现 2 2 k 2^{2^k} 22k 也就 2 16 2^{16} 216 可以接受,于是考虑对所有 2 k 2^k 2k 中取值情况下 Q Q Q 的值进行状压。

很容易想到DP处理,有两种情况,一种是前面加一个 ¬ \neg ¬ ,一种是用 → \rightarrow 连接两个公式。第一种直接转移即可,第二种是一个卷积。

乍一看转移的下标有点诡异,是 S → T S\rightarrow T ST,其实把真值表写出来就会发现就是 ( ¬ S ) ∨ T (\neg S)\lor T (¬S)T

写个FWT,中途多用点值转移,不然复杂度是假的。

然后就是枚举点值暴力模拟计算即可,我转了一下后缀表达式。

复杂度为 O ( ( n ∗ 2 k + m ) ∗ 2 2 k ) O((n*2^k+m)*2^{2^k}) O((n2k+m)22k)


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int mod=1e9+7;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;} 
inline void Mul(int &a,int b){a=mul(a,b);}
inline int po(int a,int b){int r=1;for(;b;b>>=1,Mul(a,a))if(b&1)Mul(r,a);return r;}
 
cs int N=77,SIZE=1<<(1<<4)|7;

int n,k,S;

int f[N][SIZE],g[N][SIZE],h[N][SIZE];
int A[SIZE],B[SIZE];

void FWT(int *A){
	for(int re i=1;i<S;i<<=1)
		for(int re j=0;j<S;j+=i<<1)
			for(int re k=0;k<i;++k)
				Inc(A[i+j+k],A[j+k]);
}
void IFWT(int *A){
	for(int re i=1;i<S;i<<=1)
		for(int re j=0;j<S;j+=i<<1)
			for(int re k=0;k<i;++k)
				Dec(A[i+j+k],A[j+k]);
}

void init(){
	for(int re i=0;i<k;++i){
		int s=0;
		for(int re t=0;t<(1<<k);++t)
			s|=((t>>i)&1)<<t;
		++f[0][s];
	}
	for(int re i=0;i<=n;++i){
		if(i){
			IFWT(f[i]);
			for(int re s=0;s<S;++s)
				Inc(f[i][s],f[i-1][s^(S-1)]);
		}
		memcpy(h[i],f[i],S<<2);
		memcpy(g[i],f[i],S<<2);
		std::reverse(h[i],h[i]+S);
		FWT(h[i]),FWT(g[i]);
		for(int re j=0;j<=i&&i+j+1<=n;++j)
			for(int re s=0;s<S;++s){
				Inc(f[i+j+1][s],mul(h[i][s],g[j][s]));
				if(i!=j)Inc(f[i+j+1][s],mul(g[i][s],h[j][s]));
			}
	}
}

int tp;
char s[10000];

std::string get_st(){
	scanf("%s",s+1);tp=0;
	int len=strlen(s+1);
	for(int re i=1;i<=len;++i)
		if(s[i]!='x'&&s[i]!='-')
			s[++tp]=s[i];
	static std::map<char,int> op=
		{{'(',0},{')',0},{'>',1},{'~',2}};
	std::stack<char,std::vector<char>> stk;std::string st;
	for(int re i=1;i<=tp;++i){
		char c=s[i];if(isdigit(c))--c;
		if(!op.count(c)){
			st+=c;
			if(!stk.empty()&&stk.top()=='~')
				st+=stk.top(),stk.pop();
		}else {
			if(c==')'){
				while(stk.top()!='(')
					st+=stk.top(),stk.pop();
				stk.pop();
				if(!stk.empty()&&stk.top()=='~')
					st+=stk.top(),stk.pop();
			}else stk.push(c);
		}
	}
	while(!stk.empty())
		st+=stk.top(),stk.pop();
	return st;
}

bool calc(cs std::string &st,int s,int Q){
	std::stack<bool,std::vector<bool> > stk;
	for(char c:st){
		if(c=='~'){
			bool t=stk.top();stk.pop();
			stk.push(!t);
		}else if(c=='>'){
			bool t=stk.top();stk.pop();
			bool s=stk.top();stk.pop();
			stk.push(!s||t);
		}else {
			if(isdigit(c))stk.push((s>>(c^48))&1);
			else stk.push(Q);
		}
	}return stk.top();
}

int ans;
bool ok[20][2];

void dfs(int i,int st){
	if(i==(1<<k)){Inc(ans,f[n][st]);return;}
	if(ok[i][0])dfs(i+1,st);if(ok[i][1])dfs(i+1,st|(1<<i));
}

void solve(){
	auto st=get_st();
	for(int re s=0;s<(1<<k);++s)
		ok[s][0]=calc(st,s,0),
		ok[s][1]=calc(st,s,1);
	ans=0;dfs(0,0);cout<<ans<<"\n";
}

void Main(){
	scanf("%d%d",&n,&k);
	S=1<<(1<<k);init();
	int m;scanf("%d",&m);
	while(m--)solve();
}

inline void file(){
#ifdef zxyoi
	freopen("calculus.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("calculus.in","r",stdin);
	freopen("calculus.out","w",stdout);
#endif
#endif
}signed main(){file();Main();return 0;}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值