牛客多校4 K题
题意:
给定一个合法的表达式,表达式只包含数字、加号和乘号,有
q
q
q次操作,每次操作可以选择询问一段区间内表达式的答案(保证询问区间的表达式合法)或者选择一个位置修改其为数字或加号乘号(保证修改完整个表达式合法)
题解:
显然用线段树去维护,实现单点修改和区间询问,整道题只有一个难点,如何合并两段区间的两个表达式(可能非法),解决了这个问题,显然就可以写了。考虑两个表达式合并的所有情况:
1.
1.
1.左区间和右区间无任何符号,两边分别为一个数的两半,此时合并将会合并成为一个数
2.
2.
2.左区间或右区间只有乘号,那么拼合一个数,并与左区间的乘数和右区间的乘数相乘即可
3.
3.
3.左区间或右区间存在加号,那么我们就考虑将一段区间的分为三个部分,大致形式为
A
+
B
+
C
A+B+C
A+B+C,而其中的
A
,
C
A,C
A,C我们将其当做乘法表达式,而
B
B
B作为多组表达式的和。
显然通过上述的拆解,我们可以维护以下几样东西:中间和,中间乘积,前缀长度,前缀乘积,前缀数值,后缀长度,后缀乘积,后缀数值
并将表达式分为两类去求解:
A
∗
B
∗
C
,
A
+
B
+
C
A*B*C, A+B+C
A∗B∗C,A+B+C。前者是只有乘法的表达式,后者是存在加法的表达式。
最后用线段树套表达式,利用上述规则去实现表达式合并即可。(不封装要死人)
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
#define endl '\n'
#define int long long
#define debug(p) for(auto i : p)cerr << i << " "; cerr << endl;
#define debugs(p) for(auto i : p)cerr << i.first << " " << i.second << endl;
typedef pair<int, int> pll;
string yes = "YES";
string no = "NO";
const int mod = 998244353;
constexpr int N = 5e5 + 7;
struct Modint {
int v;
Modint(int x = 0) : v(x) {
while (v < 0) {
v += mod;
}
while (v >= mod) {
v -= mod;
}
}
operator int() {
return v;
}
Modint operator+=(const Modint &b) {
if ((v += b.v) >= mod) {
v -= mod;
}
return *this;
}
Modint operator-=(const Modint &b) {
if ((v -= b.v) < 0) {
v += mod;
}
return *this;
}
Modint operator*=(const Modint &b) {
v = 1ll * v * b.v % mod;
return *this;
}
friend Modint operator+(Modint a, const Modint &b) {
return a += b;
}
friend Modint operator-(Modint a, const Modint &b) {
return a -= b;
}
friend Modint operator*(Modint a, const Modint &b) {
return a *= b;
}
};
struct Expr{
int addcnt, multicnt;
Modint midmulti, midsum, prelen, premulti, prev, suflen, sufmulti, sufv;
Expr(char c = '0')
{
addcnt = (c == '+' ? 1 : 0);
multicnt = (c == '*' ? 1 : 0);
midsum = 0;
midmulti = 1;
if(c == '*') premulti = sufmulti = 0;
else premulti = sufmulti = 1;
if(isdigit(c))
{
prev = sufv = c - '0';
prelen = suflen = 10;
}
else
{
prev = sufv = 0;
prelen = suflen = 1;
}
}
Modint query()
{
if(!addcnt)return prev * premulti;
else return prev * premulti + midsum + sufv * sufmulti;
}
friend Expr operator+(Expr const &l, Expr const &r)
{
Expr res;
res.addcnt = l.addcnt + r.addcnt;
res.multicnt = l.multicnt + r.multicnt;
if(!res.addcnt)
{
if(l.multicnt == 0) res.midmulti = r.midmulti;
else if(r.multicnt == 0) res.midmulti = l.midmulti;
else res.midmulti = l.midmulti * r.midmulti * (l.sufv * r.prelen + r.prev);
}
if(!l.addcnt)
{
if(l.multicnt == 0)
{
res.prelen = l.prelen * r.prelen;
res.premulti = r.premulti;
res.prev = l.prev * r.prelen + r.prev;
}
else
{
res.prelen = l.prelen;
res.prev = l.prev;
res.premulti = l.midmulti * (l.sufv * r.prelen + r.prev) * r.premulti;
}
}
else
{
res.prelen = l.prelen;
res.premulti = l.premulti;
res.prev = l.prev;
}
if(!r.addcnt)
{
if(r.multicnt == 0)
{
res.suflen = l.suflen * r.suflen;
res.sufmulti = l.sufmulti;
res.sufv = l.sufv * r.suflen + r.sufv;
}
else
{
res.suflen = r.suflen;
res.sufv = r.sufv;
res.sufmulti = l.sufmulti * (l.sufv * r.prelen + r.prev) * r.midmulti;
}
}
else
{
res.suflen = r.suflen;
res.sufmulti = r.sufmulti;
res.sufv = r.sufv;
}
res.midsum = l.midsum + r.midsum;
if(l.addcnt && r.addcnt) res.midsum += l.sufmulti * (l.sufv * r.prelen + r.prev) * r.premulti;
return res;
}
};
vector<Expr>t;
struct Seg_tree{
Expr s;
}tr[4 * N];
void push_up(int u)
{
tr[u].s = tr[u << 1].s + tr[u << 1 | 1].s;
}
void build(int u, int l, int r)
{
if(l == r)
{
tr[u].s = t[l];
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
push_up(u);
}
void modify(int u, int l, int r, int pos, char x)
{
if(l == r)
{
tr[u].s = Expr(x);
return;
}
int mid = l + r >> 1;
if(pos <= mid)modify(u << 1, l, mid, pos, x);
else modify(u << 1 | 1, mid + 1, r, pos, x);
push_up(u);
}
Expr query(int u, int l, int r, int ql, int qr)
{
if(ql <= l && r <= qr)return tr[u].s;
int mid = l + r >> 1;
Expr ls, rs;
if(ql <= mid)ls = query(u << 1, l, mid, ql, qr);
if(qr > mid)rs = query(u << 1 | 1, mid + 1, r, ql, qr);
if(ql <= mid && qr > mid)return ls + rs;
else if(ql <= mid) return ls;
else if(qr > mid) return rs;
}
void solve()
{
int q;
cin >> q;
string s;
cin >> s;
s = '.' + s;
int n = s.size() - 1;
for (int i = 0; i <= n; i++)
{
t.push_back(Expr(s[i]));
}
build(1, 1, n);
// cerr << "1" << endl;
while(q--)
{
int op;
cin >> op;
if(op == 1)
{
int l, r;
cin >> l >> r;
cout << query(1, 1, n, l, r).query() << endl;
}
else
{
int pos;
char x;
cin >> pos >> x;
modify(1, 1, n, pos, x);
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin>>T;
while (T--)
{
solve();
}
}