P4588 [TJOI2018] 数学计算 题解

题目要求我们维护一个初始值为1的数字 \(x\),要求支持对其乘以一个数和除以一个数,并在每一次操作后输出 \(x \bmod{M}\)

同时,题目保证我们除以的数之前被乘过,且每一个被乘过的数至多只会被除一次。
这样就可以将除法操作转化为撤回一次乘法操作。

这样我们需要做的就是,维护一串数的连乘积,同时支持向这一串数里面加入数和去掉数。

我们可以尝试使用乘法逆元做,但是这个 \(M\) 不一定是质数,所以还需要判整除,非常麻烦。
或者可以使用中国剩余定理强行来一波,但是会更麻烦。

我们可以尝试使用线段树来维护区间乘积。
首先将所有的的数值全都初始化成1,然后每一次做单点修改区间查询即可。
甚至不需要区间查询,只需要查询根节点的值就可以了。

代码如下:

#include <bits/stdc++.h>
using namespace std;
int n, mod;
const int N = 100010;
struct segtree
{
	int l, r;
	long long v;
}node[N << 2];
void build(int p, int l, int r)
{
	node[p].l = l;
	node[p].r = r;
	if (l == r)
	{
		node[p].v = 1;
		return;
	}
	int mid = (l + r) >> 1;
	build(p << 1, l, mid);
	build(p << 1 | 1, mid + 1, r);
	node[p].v = (node[p << 1].v * node[p << 1 | 1].v) % mod;
	return;
}
void segchg(int p, int ed, int k)
{
	if ((node[p].r == ed) && (node[p].l == ed))
	{
		node[p].v = k;
		return;
	}
	int mid = (node[p].r + node[p].l) >> 1;
	if (mid >= ed)segchg(p << 1, ed, k);
	if (mid < ed)segchg(p << 1 | 1, ed, k);
	node[p].v = (node[p << 1].v * node[p << 1 | 1].v) % mod;
	return;
}
int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		int m;
		scanf("%d%d", &m, &mod);
		build(1, 1, N);
		int cnt = 1;
		for (int i = 1; i <= m; i++)
		{
			int op, x;
			scanf("%d%d", &op, &x);
			if (op == 1)
			{
				segchg(1, i + 1, x);
				printf("%lld\n", node[1].v % mod);
			}
			else if (op == 2)
			{
				segchg(1, x + 1, 1);
				printf("%lld\n", node[1].v % mod);
			}
		}
	}
	return 0;
}
posted @ 2022-07-15 16:34  南陽劉子驥  阅读(23)  评论(0)    收藏  举报