An easy problem (线段树)

题目链接: An easy problem

大致题意

现在有一个初始值 X = 1. 给定操作次数Q 和 取余数M. 操作有如下两种:

​ ①输入1 y 表示给当前的X乘上一个y
​ ②输入2 y 表示给当前X除以第y次操作的操作数(保证给定的第y次操作为操作1类型)(保证不会输入相同的两个y)

要求: 在每次操作后 输出 X % M;

解题思路

最开始乍一想, 直接用小数去模拟了, 然后发现, 如果当前的X比M大了, 那么取余后可能会是个很小的数字, 这样可能导致接下来的除法操作无法完成.

然后立马想到用大数去模拟.然后就被T了… 因为这样最坏会有1E15的复杂度…

此时冷静下来仔细思考, 这道题其实有他的特点: 即他每次除的数字都是之前出现过的, 且也只会除一次.
如果不考虑除法, 我可以搞个前缀乘来得到当前操作1后的答案. 那么对于除法, 显然除法不适用于取余, 但是因为这个除数的特殊性: 是之前出现的一个乘数, 那么我们立马想到, 我们不乘以那个数字不就可以了吗?

这时, 我们就应当立刻去想到用线段树维护前缀乘, 不乘某个数字相当于把那个数字改成1即可.

==>算法: 线段树 单点修改+区间查询.

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
int n, m;
int num[N];
struct node {
    int l, r;
    ll val;
}t[N << 2];
void pushup(int x) { t[x].val = t[x << 1].val * t[x << 1 | 1].val % m; }
void build(int l, int r, int x = 1) {
    if (l == r) { t[x] = { l, r, 1 }; return; }
    t[x] = { l, r, 1 };
    int mid = l + r >> 1;
    build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
    pushup(x);
}
void modify(int a, ll c, int x = 1) {
    if (t[x].l == a && t[x].r == a) { t[x].val = c; return; }
    int mid = t[x].l + t[x].r >> 1;
    modify(a, c, x << 1 | (a > mid));
    pushup(x);
}
ll ask(int l, int r, int x = 1) {
    if (l <= t[x].l && r >= t[x].r) return t[x].val;
    int mid = t[x].l + t[x].r >> 1;
    ll res = 1;
    if (l <= mid) res = res * ask(l, r, x << 1) % m;
    if (r > mid) res = res * ask(l, r, x << 1 | 1) % m;
    return res;
}
int main()
{
    int tt; cin >> tt;
    rep(T, tt) {
        printf("Case #%d:\n", T);
        scanf("%d %d", &n, &m);
        build(1, n);
        rep(i, n) {
            int op, x; scanf("%d %d", &op, &x);
            if (op == 1) modify(i, x);
            else modify(x, 1);
            printf("%lld\n", ask(1, n) % m);
        }
    }
    return 0;
}

END

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值