解析表达式语法

牧云@^-^@ 提交于 2019-11-30 14:31:34

解析表达式语法

$ made by Ameiyo $


说白了就是暴力。


A: CF778B - Bitwise Formula

B: CF552E - Vanya and Brackets

C: CF39A - C*++ Calculations

D: CF89B - Widget Library

E: CF7E - Defining Macros

F: CF217C - Formurosa

G: CF172E - BHTML+BCSS

H: CF115D - Unambiguous Arithmetic Expression

I: CF582E - Boolean Function


A

每一位都是独立的,对每一位单独考虑。枚举问号是 0 或 1,按顺序模拟得到两个情况下 1 的个数,分情况给两个答案赋值即可。

重点还是在模拟。

#include <cstdio>
#include <cctype>
#include <map>
#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 = 5005, INF = 1e9;
const ll M = 1005;

/*******************************************************************************
 *
 * 按位枚举每一位,让每一位的 1 最少/多
 *
 ******************************************************************************/


int n, m;
int val[N][M], Ansmin[M], Ansmax[M];
int opt[N], a[N], b[N];
// ? - 0

map < string , int > mp;


int Query(int id, int vl) {
    val[0][id] = vl;
    int cnt = 0;
    rep (i, 1, n) if (opt[i] != -1) {
        if (opt[i] == 0) val[i][id] = val[a[i]][id] & val[b[i]][id];
        else if (opt[i] == 1) val[i][id] = val[a[i]][id] | val[b[i]][id];
        else if (opt[i] == 2) val[i][id] = val[a[i]][id] ^ val[b[i]][id];
        cnt += val[i][id];
    }
    return cnt;
}

void Solve() {
    rep (i, 1, m) {
        int cnt0 = Query(i, 0), cnt1 = Query(i, 1);
//         cerr << i << " " << cnt0 << " " << cnt1 << endl;
        if (cnt0 < cnt1) Ansmin[i] = 0, Ansmax[i] = 1;
        else if (cnt0 > cnt1) Ansmin[i] = 1, Ansmax[i] = 0;
        else Ansmin[i] = Ansmax[i] = 0;
    }
}


int main() {
    n = read<int>(), m = read<int>();
    string name, name1, nameopt, name2;
    rep (i, 1, n) {
        cin >> name;
        mp[name] = i;
        cin >> name >> name1;
        if (isdigit(name1[0])) {
            opt[i] = -1;
            rep (j, 1, m) val[i][j] = (name1[j - 1] ^ 48);
            continue;
        }
        cin >> nameopt >> name2;
        if (nameopt == "AND") opt[i] = 0;
        else if (nameopt == "OR") opt[i] = 1;
        else if (nameopt == "XOR") opt[i] = 2;
        if (name1 == "?") a[i] = 0;
        else a[i] = mp[name1];
        if (name2 == "?") b[i] = 0;
        else b[i] = mp[name2];
    }

    Solve();

    rep (i, 1, m) printf("%d", Ansmin[i]);
    puts("");
    rep (i, 1, m) printf("%d", Ansmax[i]);
    puts("");
    return 0;
}

B

首先括号肯定是加在两个乘号之间才能使答案更大,枚举这个区间,然后就是一遍只有两个符号的模拟了。

#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 = 5005, INF = 1e9;

/*******************************************************************************
 *
 * 枚举两个 * 号,把他们中间的括起来
 * 开头和结尾可以加哨兵
 *
 ******************************************************************************/


int n;
char s[N];
int id[N], tot;

ll Stk[N << 1], Top;
ll Solve(int l, int r) {
    int cnt = 0;
    Top = 0;
    rep (i, 1, n) {
        if (i == l + 1) {
            Stk[++Top] = -1;
        }
        if (s[i] == '*') Stk[++Top] = -2;
        else if (s[i] == '+') ++cnt;
        else {
            Stk[++Top] = (s[i] ^ 48);
        }
        if (i == r - 1) {
            for ( ; Stk[Top - 1] != -1 && Top && cnt; --Top, --cnt)
                Stk[Top - 1] = Stk[Top - 1] + Stk[Top];
            Stk[Top - 1] = Stk[Top], --Top;
        }
        if (Stk[Top - 1] == -2 && Stk[Top] >= 0)
            Stk[Top - 2] = Stk[Top - 2] * Stk[Top], Top -= 2;
    }
    for ( ; Top && cnt; --Top, --cnt)
        Stk[Top - 1] = Stk[Top - 1] + Stk[Top];
//     cerr << l << " " << r << " " << Stk[1] << endl;
    return Stk[1];
}


int main() {
    scanf("%s", s + 1);
    n = strlen(s + 1);
    id[tot = 1] = 0;
    rep (i, 1, n) if (s[i] == '*') id[++tot] = i;
    id[++tot] = n + 1;

    ll Ans = 0;
    rep (i, 1, tot) rep (j, i + 1, tot)
        Ans = max(Ans, Solve(id[i], id[j]));

    printf("%lld\n", Ans);
    return 0;
}

C

把所有的 summand 提取出来,每个前面肯定有一个系数,一个贪心,系数越小的在前面,与 前置或后置 无关。

注意提取的时候前面的两个 + 必须是没有被使用过得,否则会被 hack

#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 = 10005, INF = 1e9;

/*******************************************************************************
 *
 * 把单独的 a 变成系数为 1 的乘法
 * 所有的乘法按照系数从小到大计算(有负数)
 *
 ******************************************************************************/


#define int ll


int a, n, mark[N];
char s[N];

int tot;
struct NODE {
    int mul, opt;
    inline int operator < (const NODE &__) const {
        return mul < __.mul;
    }
} node[N];


int Solve() {
    sort(node + 1, node + tot + 1);
    int ans = 0;
    rep (i, 1, tot) {
        if (node[i].opt == 0) ++a;
        ans += node[i].mul * a;
        if (node[i].opt == 1) ++a;
    }
    return ans;
}


signed main() {
    a = read<int>();
    scanf("%s", s + 1);
    n = strlen(s + 1);
    rep (i, 1, n) if (s[i] == 'a') {
        ++tot;
        if (s[i - 1] == '+' && s[i - 2] == '+' && !mark[i - 2]) {
            node[tot].opt = 0;
            mark[i] = mark[i - 1] = mark[i - 2] = true;
            if (s[i - 3] != '*') {
                node[tot].mul = (s[i - 3] == '-' ? -1 : 1);
                continue;
            }
            int cur = i - 4, k = 1, init = 0;
            for ( ; cur > 0 && isdigit(s[cur]); --cur) ;
            if (s[cur] == '-') k = -1;
            rep (j, cur + 1, i - 4) init = init * 10 + (s[j] - '0');
            node[tot].mul = init * k;
        } else if (s[i + 1] == '+' && s[i + 2] == '+') {
            node[tot].opt = 1;
            mark[i] = mark[i + 1] = mark[i + 2] = true;
            if (s[i - 1] != '*') {
                node[tot].mul = (s[i - 1] == '-' ? -1 : 1);
                continue;
            }
            int cur = i - 2, k = 1, init = 0;
            for ( ; cur > 0 && isdigit(s[cur]); --cur) ;
            if (s[cur] == '-') k = -1;
            rep (j, cur + 1, i - 2) init = init * 10 + (s[j] - '0');
            node[tot].mul = init * k;
        }
    }

    printf("%lld\n", Solve());
    return 0;
}

D

其实题目就是这个意思

Widget [name] ([x], [y])
宽 x, 高 y, 名字 name

HBox [name]
创建横向打包器

VBox [name]
创建纵向打包器

[name1].pack([name2])
把 name2 丢到 name1 里

[name].set_border([x])
设置 border

[name].set_spacing([x])
设置 spacing

HBox:
2 * border + (cnt - 1) * spacing + Sigma(Width)
2 * border + max(Height)

VBox:
2 * border + max(Width)
2 * border + (cnt - 1) * spacing + Sigma(Height)

最后两组式子可以通过试样例试出来。

然后就是模拟。注意所有的变量都会随时改变,所以要建出一个关系最后更新一遍。

#include <cstdio>
#include <cctype>
#include <map>
#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 = 105, INF = 1e9;

/*******************************************************************************
 *
 * 大模拟
 *
 ******************************************************************************/


int n, In[N];
struct EDGE {
    int to, nex;
} edge[N];
int head[N], cntedge;
void Addedge(int u, int v) {
    edge[++cntedge] = (EDGE) { v, head[u] };
    head[u] = cntedge, ++In[v];
}

map < string , int > mp;
string name[N];

int tot;
struct Widget {
    char opt;
    int cnt;
    ll border, spacing;
    ll SumWson, SumHson;
    ll MaxWson, MaxHson;
    ll Width, Height;
    void Calc() {
        if (opt == 'W') return ;
        if (!cnt) return (void) (Width = Height = 0);
        if (opt == 'H') {
            Width = 2 * border + (cnt - 1) * spacing + SumWson;
            Height = 2 * border + MaxHson;
        } else if (opt == 'V') {
            Width = 2 * border + MaxWson;
            Height = 2 * border + (cnt - 1) * spacing + SumHson;
        }
    }
    void Pushup(const Widget son) {
        ++cnt;
        SumWson += son.Width;
        SumHson += son.Height;
        MaxWson = max(MaxWson, son.Width);
        MaxHson = max(MaxHson, son.Height);
        Calc();
    }
    void SetBorder(ll x) { border = x; }
    void SetSpacing(ll x) { spacing = x; }
} node[N];

void Widget() {
    string s;
    cin >> s;
    int len = s.length(), id = 0;
    string name = "";
    rep (i, 0, len - 1) {
        if (s[i] == '(') {
            id = i;
            break;
        }
        name += s[i];
    }
    ::name[mp[name] = ++tot] = name;
    int x = 0, y = 0;
    rep (i, id + 1, len - 1) {
        if (s[i] == ',') {
            id = i;
            break;
        }
        x = x * 10 + s[i] - '0';
    }
    rep (i, id + 1, len - 1) {
        if (s[i] == ')') {
            id = i;
            break;
        }
        y = y * 10 + s[i] - '0';
    }
    node[tot].Width = x, node[tot].Height = y;
    node[tot].opt = 'W';
//     cerr << name << " " << node[tot].Width << " " << node[tot].Height << endl;
}
void HBox() {
    string name;
    cin >> name;
    ::name[mp[name] = ++tot] = name;
    node[tot].opt = 'H';
}
void VBox() {
    string name;
    cin >> name;
    ::name[mp[name] = ++tot] = name;
    node[tot].opt = 'V';
}


void Set(string s) {
    string name = "", ord = "";
    int len = s.length(), id;
    rep (i, 0, len - 1) {
        if (s[i] == '.') {
            id = i;
            break;
        }
        name += s[i];
    }
    int idv = mp[name];
    rep (i, id + 1, len - 1) {
        if (s[i] == '(') {
            id = i;
            break;
        }
        ord += s[i];
    }
    if (ord == "pack") {
        name = "";
        rep (i, id + 1, len - 1) {
            if (s[i] == ')') {
                id = i;
                break;
            }
            name += s[i];
        }
        int idu = mp[name];
        Addedge(idu, idv);
    } else {
        int x = 0;
        rep (i, id + 1, len - 1) {
            if (s[i] == ')') {
                id = i;
                break;
            }
            x = x * 10 + s[i] - '0';
        }
        if (ord == "set_border") node[idv].border = x;
        else if (ord == "set_spacing") node[idv].spacing = x;
    }
}

void Solve() {
    int Q[N], l, r;
    l = r = 0;
    rep (i, 1, tot) if (!In[i]) Q[++r] = i;
    while (l < r) {
        int u = Q[++l];
        for (int i = head[u], v; i; i = edge[i].nex) {
            v = edge[i].to;
            node[v].Pushup(node[u]);
            if (!(--In[v])) Q[++r] = v;
        }
    }
}

int main() {
    n = read<int>();
    rep (i, 1, n) {
        string order;
        cin >> order;
        if (order == "Widget") Widget();
        else if (order == "HBox") HBox();
        else if (order == "VBox") VBox();
        else Set(order);
    }

    Solve();

    sort(name + 1, name + tot + 1);
    rep (i, 1, tot)
        cout << name[i] << " " << node[mp[name[i]]].Width << " "
        << node[mp[name[i]]].Height << endl;
    return 0;
}

E

其实也是模拟,但是有一些性质。

下面定义不安全为 没有被一对括号括起来

  1. 如果一个宏不可行,那么包含他的宏也不可行。
  2. 如果一个 / 后面有不安全的运算符,那么当前宏不可行。
  3. 如果一个 - 后面有不安全的低级运算符,那么当前宏不可行。
  4. 如果一个 * 后面有不安全的低级运算符,那么当前宏不可行。

每个宏有三个变量,分别表示 是否可行是否有不安全的运算符是否有不安全的低级运算符

在输入的时候依次处理出每个宏,最后对输入做一次相同的判断得到是否合法。

一种比较简单粗暴的方法是把整个式子中缀转后缀,然后直接暴力搞,因为只有符号 + 宏会引起歧义。

但是这样的话不安全就只能在最外层判,因为转后缀后就不清楚是否有括号。

这样打代码比较长,但是基本没有思维难度。

#include <cstdio>
#include <cctype>
#include <map>
#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 = 105, INF = 1e9;
const ll M = 1005;

int tot;
struct NODE {
    int save, opr, dop;
} node[N];

string name[N];
map < string , int > mp;
map < int , int > dep;

int OPRTPINT[M], Opr[] = { '+', '-', '*', '/' };
int chkOpr(char ch) {
    rep (i, 0, 3) if (Opr[i] == ch) return true;
    return false;
}


int n, debug;

string getName(string &s, int &cur, int len) {
    string name = "";
    rep (i, cur, len - 1) {
        if (!isdigit(s[i]) && !isalpha(s[i])) {
            cur = i - 1;
            break;
        }
        name += s[i];
        if (i == len - 1) cur = len;
    }
    return name;
}


int cntele, cntli, cntnbl, Top;
NODE ele[M], Stk[M];
int li[M], nbl[M], stk[M];
int Change(int l, int r) {
    if (li[l + 1] == -6 && li[l] >= 0) {
        int id = li[l];
        nbl[++cntnbl] = id;
        ele[id].opr = ele[id].dop = false;
        return l + 1;
    }
    int id = l;
    rep (i, l, r) {
        id = i;
        if (li[i] == -6) break;
        if (li[i] >= 0) {
            nbl[++cntnbl] = li[i];
            continue;
        }
        if (li[i] == -5) {
            i = Change(i + 1, r);
            continue;
        }
        while (Top && stk[Top] >= l && dep[li[stk[Top]]] >= dep[li[i]])
            nbl[++cntnbl] = li[stk[Top]], --Top;
        stk[++Top] = i;
    }
    while (Top && stk[Top] >= l) nbl[++cntnbl] = li[stk[Top]], --Top;
    return id;
}
NODE Get(NODE A, NODE B, int opr) {
    NODE Ans;
    Ans.save = A.save & B.save;
    if (opr == -1 && B.opr) Ans.save = false;
    if (opr == -3 && B.dop) Ans.save = false;
    if (opr == -2 || opr == -1) {
        if (A.dop || B.dop) Ans.save = false;
    }
    Ans.opr = Ans.dop = false;
    return Ans;
}
void CheckLi(int num) {
    Top = 0;
    Change(1, cntli);
    Top = 0;
    rep (i, 1, cntnbl) {
        if (nbl[i] > 0) Stk[++Top] = ele[nbl[i]];
        else Stk[Top - 1] = Get(Stk[Top - 1], Stk[Top], nbl[i]), --Top;
    }
    node[num] = Stk[1];
}
int isch(char ch) { return isdigit(ch) || isalpha(ch); }
void Get(string s, int num) {
    cntli = cntnbl = cntele = 0;
    int len = s.length(), id = -1, flag = false;
    string now = "";
    rep (i, 0, len - 1) if (s[i] != ' ') now += s[i];
    swap(now, s);
    len = s.length();
    if (s[0] == '(') {
        int cnt = 0;
        rep (i, 1, len - 1) {
            if (s[i] == '(') ++cnt;
            else if (s[i] == ')') {
                if (cnt) --cnt;
                else if (i == len - 1) flag = true;
            }
        }
        if (flag) {
            now = "", id = -1;
            rep (i, 1, len - 2) now += s[i];
            swap(now, s);
            len = s.length();
        }
    }
    for (int i = 0; i < len; ++i) {
        if (isch(s[i])) {
            string name = getName(s, i, len);
            if (mp.count(name)) id = mp[name];
            else id = 0;
            li[++cntli] = ++cntele;
            ele[cntele] = node[id];
        } else li[++cntli] = OPRTPINT[(int) s[i]];
    }
    CheckLi(num);
    int cnt = 0;
    rep (i, 1, cntli) {
        if (li[i] == ')') --cnt;
        else if (li[i] == '(') ++cnt;
        if (cnt) continue;
        if (li[i] > -5 && li[i] < 0) node[num].opr = true;
        if (li[i] > -5 && li[i] < -2) node[num].dop = true;
    }
    if (flag) node[num].opr = node[num].dop = false;
}


int main() {

    node[0].save = true;
    rep (i, 0, 3) OPRTPINT[Opr[i]] = i - 4;
    OPRTPINT[(int) '('] = -5, OPRTPINT[(int) ')'] = -6;

    dep[-1] = dep[-2] = 2, dep[-3] = dep[-4] = 1;

    n = read<int>();
    rep (i, 1, n) {
        string define;
        cin >> define;
        if (define == "#") cin >> define;
        string name;
        cin >> name;
        ::name[mp[name] = i] = name;
        string txt;
        getline(cin, txt);
        Get(txt, i);
    }

//     rep (i, 1, n)
//         cerr << node[i].save << " " << node[i].opr << " " << node[i].dop << endl;

    string txt;
    getline(cin, txt);
    Get(txt, n + 1);

    string Ans = node[n + 1].save ? "OK" : "Suspicious";
    cout << Ans << endl;
    return 0;
}

F

这其实是一道 DP ,一开始思路挺乱的,有点接近正解,但还是差了些。

假设最后的值是 $ f(s) $ , $ s $ 是每个 $ ? $ 的一组取值,那么只要存在一组 $ s $ 与 $ s' $ 满足二者至少有一个 $ ? $ 的取值不一样,而且 $ f(s) \neq  f(s') $ 我们就可以确定出这个 $ ? $ 在 $ s $ 时的值。

证明:

对于这部分表达式来说,因为 $ f(s) \neq f(s') $ ,所以元素值的变化会反应在结果上,具体的操作就是我们尝试改变某个(或几个)值,结果发现函数值改变了,因为我们时可以知道函数值的,从而就可以推断出改变的方向。

反之,如果不存在,那么无论怎么改变,我们都不知道前后的元素的值是否相同,也就不能判断值了。

但是对于异或操作来说,我们可以得知的只有一个表达式的值是否改变,而不是从 $ 0 -> 1 $ 或 $ 1 -> 0 $ ,但如果确定了某一个是 $ 0 / 1 $ ,那么这个值就可以确定了。

所以对于每一组括号,我们 DP 出这组括号的情况:

  1. 是否存在 $ s $ 使 $ f(s) = 0 $ 且 $ f(s') = 0 $ .
  2. 是否存在 $ s $ 使 $ f(s) = 1 $ 且 $ f(s') = 1 $ .
  3. 是否存在 $ s $ 使 $ f(s) \neq f(s') $ .

回到题目,因为两种值都至少存在一个,所以我们就把不同的元素不断的放到那个位置上,直到值的改变,然后确定剩下的值。

然后就有了 $ 27 $ 种转移 ( 3 种值 3 种符号) 。这里提供一种比较简洁的做法,借鉴自 Alex_McAvoy 网友的博客.

int Get(int x, int y, int opr) {
    if (opr == '|') return x | y;
    else if (opr == '&') return x & y;
    else if (opr == '^') return x ^ y;
    return 0;
}

int Work() {
    int opr = getchar();
    if (opr == '(') {
        int ls = Work();
        int opr = getchar();
        int rs = Work(), ans = 0;
        getchar();
        rep (i, 0, 3) if (ls & (1 << i))
            rep (j, 0, 3) if (rs & (1 << j))
                ans |= (1 << Get(i, j, opr));
        return ans;
    } else {
        if (opr == '1') return 8;
        else if (opr == '0') return 1;
        else return 6;
    }
}

int main() {
    read<int>();
    if (Work() & 6) puts("YES");
    else puts("NO");
    return 0;
}

G

一开始题目看错淦了好久。。。

简单的说,给出一棵树,对于每个询问输出有多少根到节点的路径满足条件。

一条路径满足条件当且仅当:

  1. 路径的最后一个节点与给定字符串的最后一个字符相同。
  2. 给定字符串是路径所组成字符串的子序列。

那么把树建出来后直接每个询问去跑就行了, Dfs 的参数中把 从根到当前节点 能匹配到的最大下标 传进去就可以了。

#include <cstdio>
#include <cctype>
#include <map>
#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 = 250005, INF = 1e9;
const ll M = 205;

string s;

int tot, node[N];
struct EDGE {
    int to, nex;
} edge[N << 1];
int head[N], cntedge;
void Addedge(int u, int v) {
    edge[++cntedge] = (EDGE) { v, head[u] };
    head[u] = cntedge;
}


map < string , int > mp;


int Stk[N], Top;
void Get(int &cur, int r) {
    string tmp = "";
    int flagCloseSelf = false, flagClose = false;
    if (s[cur] == '/') flagClose = true, ++cur;
    rep (i, cur, r) {
        cur = i;
        if (s[i] == '>') break;
        if (s[i] == '/') flagCloseSelf = true;
        else tmp += s[i];
    }
//     cerr << tmp << " " ;
    if (flagClose) {
        --Top;
    } else {
        if (mp.count(tmp)) node[++tot] = mp[tmp];
        else node[++tot] = 0;
//         cerr << tot << " " << node[tot] ;
        Addedge(Stk[Top], tot);
        if (!flagCloseSelf) Stk[++Top] = tot;
    }
//     cerr << endl;
}
void Change() {
    int len = s.length();
    for (int i = 0; i < len; ++i) {
        if (s[i] == '<') {
            int cur = i + 1;
            Get(cur, len - 1);
            i = cur;
        }
    }
}


int li[M][M], m, Ans;
void Dfs(int u, int *li, int cur) {
//     cerr << u << " " << node[u] << endl;
    if (node[u] == li[cur + 1]) ++cur;
    if (cur == li[0] && node[u] == li[cur]) ++Ans;
    for (int i = head[u], v; i; i = edge[i].nex) {
        v = edge[i].to;
        Dfs(v, li, cur);
    }
}
void Work() {
    rep (ks, 1, m) {
        Ans = 0;
        for (int i = head[0], v; i; i = edge[i].nex) {
            v = edge[i].to;
            Dfs(v, li[ks], 0);
        }
        printf("%d\n", Ans);
    }
}


int main() {

    cin.tie(0);
    ios::sync_with_stdio(false);

    int cnt = 0;
    cin >> s >> m;

    rep (i, 1, m) {
        string now, tmp;
        getline(cin, now);
        while (!now.length()) getline(cin, now);
        now += ' ';
        int len = now.length();
        rep (j, 0, len - 1) {
            if (now[j] == ' ') {
                if (!mp.count(tmp)) mp[tmp] = ++cnt;
                li[i][++li[i][0]] = mp[tmp];
                tmp = "";
                continue;
            }
            tmp += now[j];
        }
    }
    Change();

    Work();

    return 0;
}

H

其实是一个挺简单的 DP ,一开始打 区间 DP T 死我了。

仔细观察题目,可以发现原来多项式的方案数其实就是加括号的方案数。

令 $ f[i][j] $ 表示前 $ i $ 个位置,还有 $ j $ 个左括号没有匹配右括号 的方案数。

考虑两种情况:

  1. 这一位是符号,下一位是数字。

    由于每个符号所影响的两部分是必须用括号包裹的,所以这个符号的左括号只有一个选择,就是在对应的地方。

    而在下一个数字的后面,我们可以加上任意数量的右括号,只要左括号允许。因为可以把后面的数字留给后面的符号用,所以也可以不加右括号。

    所以这一步就是 $ f[i][j] = \sum f[lst][k] (k \ge j - 1) $ 。

  2. 这一位是符号,下一位是 +-

    首先,对于当前的符号来说,同样只能在也必须在一个确定的地方加左括号,而因为下一位不是数字,所以就不能加右括号,因为这会影响到下一位的状态。

    即 $ f[i][j] = f[lst][j - 1] $ 。

由于所有的数字旁边都必须加上一对括号,所以这里并不考虑。

还要判断一些不合法的情况,比如开头有 * / ,或最后一位是符号。

int main() {
    cin >> s, len = s.length();

    rep (i, 0, len - 1) {
        if (i && isdigit(s[i]) && isdigit(s[i - 1])) continue;;
        if (isdigit(s[i])) li[++n] = 1;
        else if (s[i] == '+' || s[i] == '-') li[++n] = -1;
        else if (s[i] == '*' || s[i] == '/') li[++n] = -2;
    }

    int id = 0, m = 2000;
    f[0][0] = 1;
    rep (i, 1, n) if (li[i] < 0) {
        if (i < n && li[i + 1] == -2) {
            id = i;
            continue;
        }
        if (li[i] == -2 && i == 1) {
            id = i;
            break;
        }
        if (i < n && li[i + 1] == 1) {
            int sum = f[id][m];
            dep (j, m, 1) {
                ((sum += f[id][j - 1]) >= Mod && (sum -= Mod));
                f[i][j] = sum;
            }
            f[id = i][0] = sum;
        } else {
            if (i == n) {
                id = i;
                break;
            }
            rep (j, 1, m) f[i][j] = f[id][j - 1];
            id = i;
        }
    }

    printf("%d\n", f[id][0]);
    return 0;
}

I

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

令 $ 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 $

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!