觸手不及(巴科斯範式求表達式樹)
本題為學軍神犇 cxt 出的神題。
題意
為了避免流露出自己的感情傷害別人, 小 M.M.T. 決定通過一個表達式來傳遞心意.
給出一個等式.
等式左邊是一個 \(int\) 範圍內的數, 等式右邊是一個合法的 c++ 表達式.
例如:\(233 = 66 ? 4 ? 31\)
保證等式右邊只包含數字 \(x (x ∈ [0, p),p\) 是給定的質數\()\), 加號, 減號, 乘號, 除號, 左右括號.
保證等式中沒有任何空格,tab 等不可見字符. 而且保證合法。
但是遺憾的是, 因為一些原因, 該等式不保證成立.
於是, 小 M.M.T. 希望知道, 在模 \(p\) 意義下, 她的表達式的每個數字 \(x\)
保證原不等式不存在除 \(0\), 你需要保證把數字變成 \(x\) 之後等式仍然不會除 \(0\).
如果無論 \(x\) 取多少都不能使等式成立, 則輸出 \(No~Solution\) .
如果無論 \(x\) 取多少都能使等式成立, 則輸出 \(-1\) .
\(len,p \le 5 \times 10^6\)
題解
首先很顯然的根據給出的中綴表達式來求出二叉表達式樹,如何求呢?
其實可以直接遞歸模擬這個過程。
這就是著名的 BNF(巴科斯範式) ,一種用遞歸的思想來表述計算機語言符號集的定義規範。
以下函數的名稱全部參考自 BNF 。
- 首先最外面一層是由很多 \(+,-\)
- 然後會接下來會分成很多個子表達式,每個最終會代表成一個值,我們把處理這單獨一串值稱為 \(term\) (相)。
- 每個 $term $ 是可能由 \(0\) 個,甚至多個 \(* ,/\) 連接一些數成的表達式,我們接下來就需要求那些數叫 \(factor\) (因子)
- 然後 \(factor\) 就會分成兩種情況,要麽為單獨一個數(求單個數的為 \(digit\) ),要麽就是以括號開頭,然後繼續是一個完整的表達式 \(expr\) 。
就這樣不斷遞歸處理,就可以處理出這顆二叉樹了。
說起來似乎很玄學,如果看過代碼後,就應該會覺得很好理解了。
整體思路就是,\(expr\) 實際上就是很多 \(term\) 用 \(+,-\) 連接起來的表達式。同理 \(term\) 就是很多 \(factor\) 用 \(*\) 號連接起來的乘積,\(factor\) 要麽是個 \(digit\) 要麽是個用 \(()\) 包起來的 \(expr\) 。
然後處理出這顆表達式二叉樹後,就很好做了,我們令 Dfs(o, val)
表示 \(o\) 這顆子樹(表達式)需要滿足等式成立需要的改變成 \(val\) 。
遞歸下去處理,每次用四則運算的逆運算實現就行了。(此處需要預處理每個子樹本來代表的值)
註意,葉子就是最後的數字。
然後有幾個特殊情況。
- 當前區間運算為 \(*\) ,對於其中一顆子樹來說,另一顆子樹的值為 \(0\) ,有兩種情況。
- \(val \not = 0\) ,怎麽改變都不能成立,那麽那顆子樹內所有點都為 \(No~Solution\) 。
- \(val = 0\) ,怎麽改變都可以成立,那麽那顆子樹內所有點都為 \(-1\) 。
- 當前區間運算為 \(/\) ,有兩種特殊情況。
- \(val = 0\) 並且左兒子值 \(\not = 0\) ,那麽右兒子整個無解。
- \(val \not = 0\) 並且左兒子值 \(= 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;
}
觸手不及(巴科斯範式求表達式樹)