【校內模擬】命題演算(FWT)(中綴表達式轉後綴表達式)(模擬)

簡要題意:

你有兩種可用的邏輯符號,分別是 \rightarrow¬\neg

現在有 kk 個獨立的命題變元,和一個未知的公式 QQ,你只知道 QQ 當中總共個存在恰好 nn 個上述邏輯符號,且僅會存在上述 kk 個命題變元。

mm 個詢問。每次給出一個公式 PP,且 PP 的若干部分可能被 QQ 取代。

詢問有多少種可能的 QQ 使得 PP 永真。

k4,n70,m500k\leq 4,n\leq 70,m\leq 500


題解:

本來還以爲要轉成析取範式或者合取範式來搞,仔細一想發現不可實現。

發現 kk 只有 44,也就是說 2k=162^k=16 中命題變元取值情況可以暴力枚舉,然後看允許 QQ 取哪些情況。

發現 22k2^{2^k} 也就 2162^{16} 可以接受,於是考慮對所有 2k2^k 中取值情況下 QQ 的值進行狀壓。

很容易想到DP處理,有兩種情況,一種是前面加一個 ¬\neg ,一種是用 \rightarrow 連接兩個公式。第一種直接轉移即可,第二種是一個卷積。

乍一看轉移的下標有點詭異,是 STS\rightarrow T,其實把真值表寫出來就會發現就是 (¬S)T(\neg S)\lor T

寫個FWT,中途多用點值轉移,不然複雜度是假的。

然後就是枚舉點值暴力模擬計算即可,我轉了一下後綴表達式。

複雜度爲 O((n2k+m)22k)O((n*2^k+m)*2^{2^k})


代碼:

#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;}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章