2024暑期集训补题(线段树)

牛客多校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 ABC,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();
	}
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值