简要题意:
你有两种可用的逻辑符号,分别是 → \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 k≤4,n≤70,m≤500
题解:
本来还以为要转成析取范式或者合取范式来搞,仔细一想发现不可实现。
发现 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 S→T,其实把真值表写出来就会发现就是 ( ¬ 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((n∗2k+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;}