旅行传送门:快来点我鸭
题目描述
小豆现在有一个数 x,初始值为 1。小豆有 Q 次操作,操作有两种类型:
1 m:将 x 变为 x * m,并输出 x mod M
2 pos:将 x 变为 x 除以第 pos 次操作所乘的数(保证第 pos 次操作一定为类型 1,对于每一个类型 1 的操作至多会被除一次),并输出 x mod M。
输入格式
一共有 t 组输入。
对于每一组输入,第一行是两个数字 Q,M。
接下来 Q 行,每一行为操作类型 op,操作编号或所乘的数字 m(保证所有的输入都是合法的)。
输出格式
对于每一个操作,输出一行,包含操作执行后的 x mod M 的值。
输入输出样例
输入 #1复制
1
10 1000000000
1 2
2 1
1 2
1 10
2 3
2 4
1 6
1 7
1 12
2 7
输出 #1复制
2
1
2
20
10
1
6
42
504
84
说明/提示
对于 20% 的数据,1 ≤ Q ≤ 500。
对于 100% 的数据,1 ≤ Q ≤ 10^5,t ≤ 5,M ≤ 10^9。
解题思路
第一眼看去,就只对一个数进行操作,这哪是甚么线段树的题,直接上暴力模拟不就完事了(上了就去见祖宗,哪怕开long long也直接亲ma炸穿)(虽然纯当作数论题做也不是不行)。
但转念一想,蓝题不可能这么水,重新审题,题目中有两个操作,一是x乘以一个值,另一个是x除以之前乘过的某个值;乘以一个数又除以一个数,相当于啥也没干,也就可以看成是回溯操作。那么我们不妨以时间为轴建立一棵范围为[1,Q]的线段树,第k个叶子节点的序号表示这是第pos次操作,其存储的值表示x在这次操作中乘以的数。此时第一种操作变为给当前叶节点赋值m,第二种操作变为令第pos个叶节点的值回溯至1,根节点的值就是每次操作执行后x的值。
总的来说是道很有意思的题_(:з」∠)_
AC代码
#include <bits/stdc++.h>
#define FOR(i, j, k) for (int i = (j); i <= (k); i++)
#define MAXQ 100000 + 10
typedef long long ll;
ll t, q, mod;
ll tree[MAXQ << 2];
ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
void build(ll k, ll l, ll r)
{
tree[k] = 1;
if (l == r)
return;
ll mid = (l + r) >> 1;
build(k << 1, l, mid);
build(k << 1 | 1, mid + 1, r);
}
void update(ll k, ll l, ll r, ll pos, ll x)
{
if (l == r)
{
tree[k] = x;
return;
}
ll mid = (l + r) >> 1;
if (pos <= mid)
update(k << 1, l, mid, pos, x);
else
update(k << 1 | 1, mid + 1, r, pos, x);
tree[k] = ((tree[k << 1] % mod) * (tree[k << 1 | 1] % mod)) % mod;
}
int main(int argc, char const *argv[])
{
t = read();
while (t--)
{
q = read(), mod = read();
build(1, 1, q);
FOR(i, 1, q)
{
ll op = read();
if (op == 1)
{
ll m = read();
update(1, 1, q, i, m);
printf("%lld\n", tree[1] % mod);
}
else
{
ll pos = read();
update(1, 1, q, pos, 1);
printf("%lld\n", tree[1] % mod);
}
}
}
return 0;
}