• CF582E Boolean Function


    CF582E - Boolean Function

    \[made \ by \ Ameiyo \]


    首先将表达式转换成表达式树。

    令 $ f[u][s] $ 表示 $ u $ 这棵子树中,在 $ n $ 组取值的情况下, $ n $ 组结果为 $ s $ 的方案数。

    首先每一位的转移是互不影响的,因为只是把他们放到一起而已,所以 $ s1, s2 $ 与 符号 $ opr $ 所转移到的地方就是 $ s1 \ opr \ s2 $ 。

    所以可以先写出一个暴力算法。

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    #define ll long long
    #define reg register
    #define rep(i, a, b) for (reg int i = (a), i##end = (b); i <= i##end; ++i)
    #define dep(i, a, b) for (reg int i = (a), i##end = (b); i >= i##end; --i)
    
    template <typename _typer> inline _typer read() {
        _typer init = 0;
        char ch = getchar(), k = 0;
        for ( ; !isdigit(ch); ch = getchar()) k = (ch == '-');
        for ( ; isdigit(ch); ch = getchar())
            init = (init << 3) + (init << 1) + (ch ^ 48);
        return k ? -init : init;
    }
    const ll N = 505, INF = 1e9;
    const ll M = (1 << 16), Mod = 1e9 + 7;
    
    /*******************************************************************************
     *
     *
     *
     ******************************************************************************/
    
    int n, tot;
    int lson[N], rson[N], node[N], f[N][M], ChTpInt[N], cnt[N];
    
    string s;
    struct NODE { int id, rt; } ;
    NODE Change(int l, int r) {
        int u = ++tot, id = l;
    //     cerr << l << " " << r << endl;
        rep (i, l, r) {
            id = i;
            if (s[i] == '(') {
                NODE tmp = Change(i + 1, r);
                if (lson[u]) rson[u] = tmp.rt;
                else lson[u] = tmp.rt;
                id = i = tmp.id;
                continue;
            }
    //         cerr << s[i] << endl;
            if (s[i] == ')') break;
            if (s[i] != '?') {
                node[u] = s[i];
            } else if (s[i + 1] == '(') { // opr - ?
                node[u] = 1;
            } else { // num - ?
                node[u] = 2;
            }
        }
    //     cerr << u << " " << node[u] << " " << id << endl;
    //     system("pause");
        return (NODE) { id, u };
    }
    
    
    int val[N][10], ans[N];
    int Get(int x, int y, int ch) {
        if (ch == '|') return x | y;
        else if (ch == '&') return x & y;
        return 0;
    }
    void Upd(int &x, int y) { ((x += y) >= Mod && (x -= Mod)); }
    void Dfs(int u) {
    //     cerr << u << " " << ((node[u] == 1 || node[u] == 2) ? node[u] : (char) node[u]) << endl;
        if (!u) return ;
        if (node[u] == 2) {
            rep (i, 1, 8) {
                int w = 0;
                rep (j, 1, n) if (val[j][i]) w |= (1 << j >> 1);
                cnt[u] += (!f[u][w]), ++f[u][w];
            }
        } else if (isalpha(node[u])) {
            int w = 0;
            rep (j, 1, n) if (val[j][ChTpInt[node[u]]]) w |= (1 << j >> 1);
            cnt[u] += (!f[u][w]), ++f[u][w];
        } else {
            int ls = lson[u], rs = rson[u];
            Dfs(ls), Dfs(rs);
            if (cnt[ls] > cnt[rs]) swap(ls, rs);
            rep (s1, 0, (1 << n) - 1) if (f[ls][s1]) {
                rep (s2, 0, (1 << n) - 1) if (f[rs][s2]) {
                    int tmp = 1ll * f[ls][s1] * f[rs][s2] % Mod;
                    if (node[u] == 1) {
                        Upd(f[u][Get(s1, s2, '|')], tmp);
                        Upd(f[u][Get(s1, s2, '&')], tmp);
                    } else Upd(f[u][Get(s1, s2, node[u])], tmp);
                }
            }
        }
    }
    
    
    int main() {
        cin >> s;
        n = read<int>();
        rep (i, 0, 3) ChTpInt['A' + i] = i + 1, ChTpInt['a' + i] = i + 5;
        int w = 0;
        rep (i, 1, n) {
            rep (d, 1, 4) val[i][d + 4] = (val[i][d] = read<int>()) ^ 1;
            if (read<int>()) w |= (1 << i >> 1);
        }
    
        NODE tmp = Change(0, s.length() - 1);
    //     cerr << "Change end" << endl;
    
        Dfs(tmp.rt);
    
        printf("%d\n", f[tmp.rt][w]);
        return 0;
    }
    
    

    但是这会血 T ,所以要对整个方案进行改进。

    先只考虑 &

    我们要统计的是满足 $ s = s1 & s2 $ 的 $ f[ls][s1] * f[rs][s2] $ 的和,因为 $ s = s1 & s2 $ ,所以 $ s $ 肯定是 $ s1 , s2 $ 最大公共子集。

    令 $ sum[s] $ 表示所有包含 $ s $ 的状态的和, $ tmp[s] $ 表示 & 之后状态为 $ s $ 的总和。

    那么 $ tmp[s] = sum[ls][s] * sum[rs][s] - \sum tmp[s'] $ ,其中 $ s \subset s' $ 。

    为什么要减去包含 $ s $ 的值?因为包含 $ s $ 的两个数可能还有其他公共部分,那么他们 & 之后的结果就是 $ s' $ 了。

    关于 $ sum $ 数组我们可以用 高维前(后)缀和 简单的解决掉,但是该如何容斥出 $ tmp $ 数组呢?

    我直接类似高维前缀和弄了个高维前缀减,尽管并不知道为什么能这么用, 但是 $ AC $ 了就是好算法(停停停)

    考虑两个状态 $ s, s' $ ,其中 $ s \subset s' $ ,设从 $ s $ 到 $ s' $ 需要 $ t $ 步。

    因为我们把高维前缀和中的 + 变成了 - ,所以因为负负得正, $ s $ 对 $ s' $ 做的贡献就是 $ (-1) ^ t $ 。

    $ sum[ls][s] * sum[rs][s] $ 的到的结果是包含所有 $ tmp[s'] (s \in s') $ 的,所以一开始的值都包含了所有的 "孩子" (在上面的博客中我将包含与不包含定义成了 父子关系)。

    对于所有 $ s'' $ 满足 二进制位中 只有 那 $ t $ 位上与 $ s $ 有 $ i $ 位不同(即那 $ i $ 位值为 $ 1 $ ,其他几位和 $ s $ 一模一样) ,会对 $ s' $ 贡献 $ s $ , 而每个的贡献是 $ (-1) ^ {t - i} $ ,对于每个 $ i $ 一共有 $ C_{t} ^{i} $ 个这样的数,所以 $ s $ 在 $ s' $ 的系数就是 $ \sum _{i = 0} ^{t} (C_{t} ^{i} * (-1) ^{t - i}) $ (在更新之前系数是 $ 1 $ )。

    那个时候还根本不会算这个东西,今天打题解的时候突然想到了前两天刚看的多项式定理,当然这里只用到了 二项式定理。

    二项式定理:

    \[(x + c) ^t = \sum _{i = 0} ^{t} (C _{t} ^{i} * x^i * c^{t - i}) \]

    那么当 $ x = 1, c = -1 $ 时,右边就是上面的式子,而值 $ (1 - 1) ^ t = 0 $ ,即最后在 $ tmp[s'] $ 中不包含 $ tmp[s] $ 。

    即证。

    (其实还有一种方法,把减法考虑成加法的逆操作,即按照加法的顺序倒着来一遍,就是一个个减掉。而我们的初始状态与加法的终止状态每个值是一致的,所以减法的终止状态就是加法的初始状态。和我说这种方法的那个人没有打博客,所以我在这里提一下)

    还要考虑 | 运算。其实或运算就是 补集做了与运算后再取补集,即 $ C _U (s1 | s2) = C _U s1 & C _U s2 $ 。

    所以转换成 & 就行了。

    void Dfs(int u) {
        if (!u) return ;
        memset(f, 0, sizeof f);
        if (node[u] == 2) {
            rep (i, 1, 8) {
                int w = 0;
                rep (j, 1, n) if (val[j][i]) w |= (1 << j >> 1);
                ++f[w];
            }
        } else if (isalpha(node[u])) {
            int w = 0;
            rep (j, 1, n) if (val[j][ChTpInt[node[u]]]) w |= (1 << j >> 1);
            ++f[w];
        } else {
            int ls = lson[u], rs = rson[u];
            Dfs(ls), Dfs(rs);
            dep (s, uplim, 0) {
                tmpS[s] = 1ll * S[ls][s] * S[rs][s] % Mod;
                tmpC[s] = 1ll * C[ls][s] * C[rs][s] % Mod;
            }
            rep (i, 0, n - 1) rep (s, 0, uplim)
                if (!(s & (1 << i))) {
                    Upd(tmpS[s], Mod - tmpS[s | (1 << i)]);
                    Upd(tmpC[s], Mod - tmpC[s | (1 << i)]);
                }
            rep (s, 0, uplim) {
                if (node[u] == 1) {
                    // |
                    f[s] = tmpC[uplim ^ s];
                    // &
                    Upd(f[s], tmpS[s]);
                } else if (node[u] == '|') {
                    f[s] = tmpC[uplim ^ s];
                } else if (node[u] == '&') {
                    f[s] = tmpS[s];
                }
            }
        }
        rep (s, 0, uplim) S[u][s] = C[u][uplim ^ s] = f[s];
        rep (i, 0, n - 1) rep (s, 0, uplim)
            if (!(s & (1 << i))) {
                Upd(S[u][s], S[u][s | (1 << i)]);
                Upd(C[u][s], C[u][s | (1 << i)]);
            }
    }
    
    

    $ in \ 2019.9.27 $

  • 相关阅读:
    mybatis plus QuerManager使用 FIND_IN_SET
    MySQL空间函数ST_Distance_Sphere()的使用
    Java8新特性 04 方法引用06 方式五 【类名[]::new
    BigDecimal保留2位小数的处理
    使用spring validation完成数据后端校验和手动调用
    HashSet和CopyOnWriteArraySet
    mysql随机取数据优化
    SpringBoot 四种获取ApplicationContext的方式
    SpringBoot 集成 WebSocket,实现后台向前端推送信息
    js some函数用法,检测数组中某元素是否符合所指定规则(规则函数可自定义)
  • 原文地址:https://www.cnblogs.com/Ameiyo/p/11597672.html
Copyright © 2020-2023  润新知