2020提高组模拟赛1 T2 expression
\(\text{Problem}\):[2020提高组模拟赛1]expression
\(\text{Solution}\):
对于表达式的求值,有两种方法。一种是用栈求出它的后缀表达式然后计算,另一种则是建出它的表达式二叉树然后遍历求解。而本题要求对于表达式中的每个数进行修改,所以我们选择建立表达式二叉树的方法,可以快速得到子树内被修改过后的答案。
首先考虑本题中的运算优先级:
则考虑递归这个表达式。最外层的显然是 \(+,-\) 这两个优先级最低的符号。对于每个 \(+,-\) 符号两边(如果根本就没出现,那就直接递归整个表达式,下同)的表达式,将考虑到它们由 \(*,/\) 构成。对于每个 \(*,/\) 符号两边的表达式,如果它是一个数字,可以直接计算出它的值;如果它是由一对括号组成的表达式,则会重新跳回到最外层,对这个括号表达式内部进行求解。这样就可以建出一棵表达式二叉树。
考虑 \(\text{DFS}\) 进行求解。记 \(w=\text{DFS(x)}\) 表示 \(x\) 的子树表达式需要改变成 \(w\) 才能使等号右侧表达式等于左侧的数 \(lval\),则可以 \(\text{DFS(root,lval)}\),当 \(x\) 为叶子节点时(由于表达式二叉树的构建方式,一个节点的孩子个数为 \(0\) 或 \(2\)),则应该将这个位置的数改变为 \(w\)。由于遍历到表达式二叉树中某个叶子节点的数的顺序和在原中缀表达式中该数出现的顺序相同,则可以直接记录后输出。
考虑如何求出 \(w=\text{DFS(x)}\) 。记每个节点的值为 \(f[x]\),左右孩子分别为 \(ch[x][0],ch[x][1]\),则有:
对于加法,\(w-f[ch[x][1]]=\text{DFS(ch[x][0])},w-f[ch[x][0]]=\text{DFS(ch[x][1])}\)。
对于减法,左孩子的值为被减数,右孩子的值为减数,则 \(w+f[ch[x][1]]=\text{DFS(ch[x][0])},f[ch[x][0]]-w=\text{DFS(ch[x][1])}\)。
对于乘法,处理方式与加法差不多,只要先预处理出每个小于模数的值的逆元即可。但是需要考虑特殊情况。对于遍历左孩子时,如果右孩子的值为 \(0\),则当 \(w=0\) 时,左孩子的子树内每个数的答案都为 \(-1\);当 \(w\not=0\) 时,左孩子的子树内每个数的答案都为 \(\text{No}\) \(\text{Solution}\)。
对于除法,由于保证了除数不为 \(0\),所以可以直接得到 \(w\times f[ch[x][1]]=\text{DFS(ch[x][0])}\)。对于遍历右孩子时,如果 \(w\) 和 \(f[ch[x][0]]\) 都为 \(0\),则右孩子的子树内每个数的答案都为 \(-1\);如果其中一个为 \(0\),则右孩子的子树内每个数的答案都为 \(\text{No}\) \(\text{Solution}\);否则直接遍历。
本题没有说明 \(0=0/x\) 这种情况应该输出什么,有点问题,然而数据中也没有这种情况。
\(\text{Code}\):
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <bitset>
#define ri register
#define inf 0x7fffffff
#define E (1)
#define mk make_pair
#define int long long
//#define double long double
using namespace std; const int N=5000010;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch-'0'), ch=getchar();
return s*w;
}
void print(int x) { if(x<0) x=-x, putchar('-'); if(x>9) print(x/10); putchar(x%10+'0'); }
//构建表达式二叉树。+- -> */ -> () -> number
int n,Mod,tot,fac[N/5],inv[N/5],cnt,ch[N][2],f[N],ot[N],m; char s[N],p[N];
inline int ksc(int x,int p) { int res=1; for(;p;p>>=1, x=x*x%Mod) if(p&1ll) res=res*x%Mod; return res; }
inline int Brackets();
inline int MulWithDiv();
inline int AddWithSub();
inline int Brackets()
{
int x=cnt;
if(isdigit(s[tot]))
{
int ans=0;
for(;isdigit(s[tot]);tot++) ans=(ans<<3)+(ans<<1)+(s[tot]-'0');
f[++cnt]=ans;
x=cnt;
}
else if(s[tot]=='(') tot++, x=AddWithSub(), tot++;
return x;
}
inline int MulWithDiv()
{
int L=Brackets();
int x=L;
for(;s[tot]=='*'||s[tot]=='/';)
{
p[++cnt]=s[tot], tot++;
x=cnt;
ch[x][0]=L, ch[x][1]=Brackets();
if(p[x]=='*') f[x]=(f[ch[x][0]]*f[ch[x][1]])%Mod;
else f[x]=(f[ch[x][0]]*inv[f[ch[x][1]]])%Mod;
L=x;
}
return x;
}
inline int AddWithSub()
{
int L=MulWithDiv();
int x=L;
for(;s[tot]=='+'||s[tot]=='-';)
{
p[++cnt]=s[tot], tot++;
x=cnt;
ch[x][0]=L, ch[x][1]=MulWithDiv();
if(p[x]=='+') f[x]=(f[ch[x][0]]+f[ch[x][1]])%Mod;
else f[x]=(f[ch[x][0]]-f[ch[x][1]]+Mod)%Mod;
L=x;
}
return x;
}
void FTree(int x,int W)
{
if(!x) return;
if(!ch[x][0]&&!ch[x][1]) return (void)(ot[++m]=W);
FTree(ch[x][0],W);
FTree(ch[x][1],W);
}
void GetAns(int goal,int x)
{
if(!x) return;
if(!ch[x][0]&&!ch[x][1]) return (void)(ot[++m]=goal);
if(p[x]=='+')
{
GetAns((goal-f[ch[x][1]]+Mod)%Mod,ch[x][0]);
GetAns((goal-f[ch[x][0]]+Mod)%Mod,ch[x][1]);
}
if(p[x]=='-')
{
GetAns((goal+f[ch[x][1]])%Mod,ch[x][0]);
GetAns((f[ch[x][0]]-goal+Mod)%Mod,ch[x][1]);
}
if(p[x]=='*')
{
if(!f[ch[x][1]])
{
int W;
if(goal) W=-1e9;
else W=-2e9;
FTree(ch[x][0],W);
}
else GetAns((goal*inv[f[ch[x][1]]])%Mod,ch[x][0]);
if(!f[ch[x][0]])
{
int W;
if(goal) W=-1e9;
else W=-2e9;
FTree(ch[x][1],W);
}
else GetAns((goal*inv[f[ch[x][0]]])%Mod,ch[x][1]);
}
if(p[x]=='/')
{
GetAns((goal*f[ch[x][1]])%Mod,ch[x][0]);
if( !goal && !f[ch[x][0]])
{
int W=-2e9;
FTree(ch[x][1],W);
}
else if(!goal || !f[ch[x][0]])
{
int W=-1e9;
FTree(ch[x][1],W);
}
else GetAns((inv[goal]*f[ch[x][0]])%Mod,ch[x][1]);
}
}
signed main()
{
n=read(), Mod=read();
inv[0]=inv[1]=1;
for(ri int i=2;i<Mod;i++) inv[i]=inv[Mod%i]*(Mod-Mod/i)%Mod;
scanf("%s",s);
int lans=0;
for(;isdigit(s[tot]);tot++) lans=(lans<<3)+(lans<<1)+(s[tot]-'0');
tot++; lans%=Mod;
int rans=AddWithSub();
GetAns(lans,rans);
for(ri int i=1;i<=m;i++)
{
if(ot[i]==-1e9) puts("No Solution");
else if(ot[i]==-2e9) puts("-1");
else printf("%lld\n",ot[i]);
}
return 0;
}