如果只是进行操作一的话,那就是很简单的操作每一次直接乘就好了,但是我们还要进行操作二:要把前某几次乘的数除掉,所以我们就需要把每一次乘的数都存储下来,且是按顺序的。我们可以想到用一个数组或者是\(vector\)来顺序存储。但是我们会发现,这样的话我们每一次输出的时候就需要把所有的数都乘起来那这个操作就是\(O(q)\)的时间复杂度。再加上我们有\(T\)组测试数据,所以这样用顺序存储结构存储乘数的话,最后的时间复杂度就会达到\(O(Tq ^ 2)\)。所以我们就需要去思考更优的存储结构,让我们每次查询的时间复杂度降下来,而线段树刚好可以实现这一点。
我们可以每一次都将值上传到根节点,这样现在的\(x\)就是我们的\(tr[1].val\),第\(i\)次存的乘数就是\(tr[i].val\),如果我们想要把第\(k\)次的乘数除掉的话,我们就只需要将\(tr[k].val\)赋值成\(1\)就可以了。也就是说我们只需要每次单点修改就可以实现我们想要的目的。
struct Seg{ int l, r; i64 val; } tr[N << 2]; int q, mod; void pushup(int u) { tr[u].val = (tr[u << 1].val * tr[u << 1 | 1].val) % mod; } void build(int u, int l, int r) { tr[u] = {l, r, 1}; if (l == r) return ; int mid = (l + r) >> 1; build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); pushup(u); } void modify(int u, int x, i64 v) { if (tr[u].l == tr[u].r) { tr[u].val = v; return ; } int mid = (tr[u].l + tr[u].r) >> 1; if (mid >= x) modify(u << 1, x, v); else modify(u << 1 | 1, x, v); pushup(u); } void solve() { std::cin >> q >> mod; build(1, 1, q); rep(i,1,q + 1) { i64 op, m; std::cin >> op >> m; if (op == 1) modify(1, i, m), tr[1].val %= mod; else { modify(1, m, 1); tr[1].val %= mod; } std::cout << tr[1].val << "\n"; } }