P4588 [TJOI2018]数学计算 题解

旅行传送门:快来点我鸭

题目描述
小豆现在有一个数 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值