触手不及(巴科斯范式求表达式树)

本题为学军神犇 cxt 出的神题。

题意

为了避免流露出自己的感情伤害别人, 小 M.M.T. 决定通过一个表达式来传递心意.

给出一个等式.

等式左边是一个 int 范围内的数, 等式右边是一个合法的 c++ 表达式.

例如:233=66431

保证等式右边只包含数字 x(x[0,p),p 是给定的质数), 加号, 减号, 乘号, 除号, 左右括号.

保证等式中没有任何空格,tab 等不可见字符. 而且保证合法。

但是遗憾的是, 因为一些原因, 该等式不保证成立.

于是, 小 M.M.T. 希望知道, 在模 p 意义下, 她的表达式的每个数字 x ,需要变成多少才能使等式成立.

保证原不等式不存在除 0, 你需要保证把数字变成 x 之后等式仍然不会除 0.

如果无论 x 取多少都不能使等式成立, 则输出 No Solution .

如果无论 x 取多少都能使等式成立, 则输出 1 .

len,p5×106

题解

首先很显然的根据给出的中缀表达式来求出二叉表达式树,如何求呢?

其实可以直接递归模拟这个过程。

这就是著名的 BNF(巴科斯范式) ,一种用递归的思想来表述计算机语言符号集的定义规范。

以下函数的名称全部参考自 BNF

  1. 首先最外面一层是由很多 +, 号构成的优先级最低的符号,我们最外层处理这个,称为 expr (表达式)。
  2. 然后会接下来会分成很多个子表达式,每个最终会代表成一个值,我们把处理这单独一串值称为 term (相)。
  3. 每个 term 是可能由 0 个,甚至多个 ,/ 连接一些数成的表达式,我们接下来就需要求那些数叫 factor (因子)
  4. 然后 factor 就会分成两种情况,要么为单独一个数(求单个数的为 digit ),要么就是以括号开头,然后继续是一个完整的表达式 expr

就这样不断递归处理,就可以处理出这颗二叉树了。

说起来似乎很玄学,如果看过代码后,就应该会觉得很好理解了。

整体思路就是,expr 实际上就是很多 term+, 连接起来的表达式。同理 term 就是很多 factor 号连接起来的乘积,factor 要么是个 digit 要么是个用 () 包起来的 expr

然后处理出这颗表达式二叉树后,就很好做了,我们令 Dfs(o, val) 表示 o 这颗子树(表达式)需要满足等式成立需要的改变成 val

递归下去处理,每次用四则运算的逆运算实现就行了。(此处需要预处理每个子树本来代表的值)

注意,叶子就是最后的数字。

然后有几个特殊情况。

  1. 当前区间运算为 ,对于其中一颗子树来说,另一颗子树的值为 0 ,有两种情况。
    • val0 ,怎么改变都不能成立,那么那颗子树内所有点都为 No Solution
    • val=0 ,怎么改变都可以成立,那么那颗子树内所有点都为 1
  2. 当前区间运算为 / ,有两种特殊情况。
    • val=0 并且左儿子值 0 ,那么右儿子整个无解。
    • val0 并且左儿子值 =0 ,那么右儿子也是无解。

标程少判了最后一个情况,数据出锅了,差评。

代码

其实还是挺好 写的。

#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl #define DEBUG(...) fprintf(stderr, __VA_ARGS__) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48); return x * fh; } void File() { #ifdef zjp_shadow freopen ("expression.in", "r", stdin); freopen ("expression.out", "w", stdout); #endif } const int N = 5e6 + 1e3, Maxn = N << 1; int Mod, Inv[N]; char str[N]; #define ls(o) ch[o][0] #define rs(o) ch[o][1] inline int Add(int a, int b) { return (a += b) >= Mod ? a - Mod : a; } namespace Expression { char *Head; int Size, ch[Maxn][2], val[Maxn], opt[Maxn]; inline void Push_Up(int o) { if (opt[o] == 0) val[o] = Add(val[ls(o)], val[rs(o)]); if (opt[o] == 1) val[o] = Add(val[ls(o)], Mod - val[rs(o)]); if (opt[o] == 2) val[o] = 1ll * val[ls(o)] * val[rs(o)] % Mod; if (opt[o] == 3) val[o] = 1ll * val[ls(o)] * Inv[val[rs(o)]] % Mod; } inline int Digit(); inline int Factor(); inline int Term(); inline int Expr(); inline int Digit() { int res = 0; for (; isdigit(*Head); ++ Head) res = (res * 10) + (*Head ^ 48) ; return res; } inline int Factor() { int Node = 0; if (*Head == '(') ++ Head, Node = Expr(), ++ Head; else val[Node = ++ Size] = Digit(); return Node; } inline int Term() { int Last = Factor(), Node = Last; while (*Head == '*' || *Head == '/') { opt[Node = ++ Size] = 2 + (*Head ++ == '/'); ls(Node) = Last; rs(Node) = Factor(); Push_Up(Last = Node); } return Node; } inline int Expr() { int Last = Term(), Node = Last; while (*Head == '+' || *Head == '-') { opt[Node = ++ Size] = (*Head ++ == '-'); ls(Node) = Last; rs(Node) = Term(); Push_Up(Last = Node); } return Node; } void Print(int o, const char* ans) { if (!ls(o) && !rs(o)) return (void) puts(ans); Print(ls(o), ans); Print(rs(o), ans); } void Dfs(int o, int Val) { if (!ls(o) && !rs(o)) return (void) printf ("%d\n", Val); if (opt[o] == 0) { Dfs(ls(o), Add(Val, Mod - val[rs(o)])); Dfs(rs(o), Add(Val, Mod - val[ls(o)])); } if (opt[o] == 1) { Dfs(ls(o), Add(Val, val[rs(o)])); Dfs(rs(o), Add(val[ls(o)], Mod - Val)); } if (opt[o] == 2) { For (dir, 0, 1) if (val[ch[o][dir ^ 1]]) Dfs(ch[o][dir], 1ll * Val * Inv[val[ch[o][dir ^ 1]]] % Mod); else Print(ch[o][dir], Val ? "No Solution" : "-1"); } if (opt[o] == 3) { Dfs(ls(o), 1ll * Val * val[rs(o)] % Mod); if (Val && val[ls[o]]) Dfs(rs(o), 1ll * val[ls(o)] * Inv[Val] % Mod); else Print(rs(o), "No Solution"); } } char Sign[4] = {'+', '-', '*', '/'}; void Out(int o) { if (!o) return ; if (!ls(o) && !rs(o)) return (void) printf (" %d ", val[o]); Out(ls(o)); putchar (Sign[opt[o]]); Out(rs(o)); } }; void Solve() { using namespace Expression; Head = str; int Base = Digit() % Mod; ++ Head; int root = Expr(); Dfs(root, Base); } int main () { File(); read(); Mod = read(); scanf ("%s", str); Inv[0] = Inv[1] = 1; For (i, 2, Mod - 1) Inv[i] = 1ll * Inv[Mod % i] * (Mod - Mod / i) % Mod; Solve(); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9676609.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(692)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示