CF582E Boolean Function(DP,状态压缩,FMT)

简单题。

我第二道自己做出来的 2900

没毛病,我没切过 2800 的题

lqy:“CF 评分 2800 是中等难度”

我活个啥劲啊

为了方便(同时压缩状态个数),先建出表达式树,然后一棵子树就代表一个完整的表达式(要么是单个变量,要么是被一堆匹配的括号恰好包住的)。

注意:(在我的写法里面)叶子都是变量,非叶子都是运算符,每个非叶子有恰好两个儿子,就表示这个运算符把整个表达式分成了哪两个,

然后就开始 DP 了。

明显状压吧?首先四个变量的取值可以压成一个四位二进制数(\(0\)\(15\))。然后再状压一次,压成一个 \(16\) 位二进制数,第 \(i\) 位表示四个变量取值情况是 \(i\) 时表达式的值。

\(dp[u][i]\) 就表示 \(u\) 为根的子树,然后)&@!$^(&*!@#为 \(i\) 的方案数。

暴力转移是 \(O(|s|4^{16})\),不太行。

发现转移是个与卷积和或卷积的形式,FMT 优化。

时间复杂度是 \(O(|s|\times 16\times 2^{16})\),虽然看起来不太稳但是跑得飞快(400ms)……


代码

#include
using namespace std;
const int maxn=5555,mod=1000000007,lim=65536;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int n,dp[255][lim],m,a[16],b[16],with[maxn],stk[maxn],tp,cnt,ch[maxn][2],fa[maxn],id[maxn],rt,p[lim],q[lim];
char s[maxn],val[maxn];
bool op[maxn];
void FMT_pre(int *A){
    for(int i=1;ii)) j++;
        id[j]=id[with[j]]=cnt;
    }
    FOR(i,1,n) if(s[i]!='(' && s[i]!=')'){
        if(s[i-1]==')' && s[i+1]=='('){
            fa[id[i-1]]=fa[id[i+1]]=id[i];
            ch[id[i]][0]=id[i-1];
            ch[id[i]][1]=id[i+1];
            op[id[i]]=1;
        }
    }
    FOR(i,1,cnt) if(!fa[i]) assert(!rt),rt=i;
    dfs(rt);
    int ans=0;
    FOR(i,0,lim-1){
        bool flag=true;
        FOR(j,0,m-1) if(((i>>a[j])&1)!=b[j]) flag=false;
        if(flag) ans=(ans+dp[rt][i])%mod;
    }
    printf("%d\n",ans);
}

你可能感兴趣的:(CF582E Boolean Function(DP,状态压缩,FMT))