码迷,mamicode.com
首页 > 其他好文 > 详细

9.9 另一个区间问题

时间:2019-09-12 00:01:57      阅读:98      评论:0      收藏:0      [点我收藏+]

标签:长度   tin   var   线段树   一个   ==   inline   序列   for   

题意

有一个长度为\(n\)的正整数序列\(a\),可以进行\(m\)次操作,每次操作有两种类型:

  • \(op=1\)

    修改操作,表示将区间\([L,R]\)内的所有数都乘上一个正整数\(x\)

  • \(op=2\)

    询问操作,询问\([L,R]\)区间中所有数乘积的欧拉函数,即\(\varphi(\prod_{i=L}^R a_i)\)

答案取模\(mod=998244353\)

\(n,m\leq 4\times 10^5,x\leq 300\)


解法

区间操作,想到用线段树维护

现在主要是求\(\varphi\)

众所周知,对于\(\varphi(n)\)我们有如下的计算公式
\[ \varphi(n)=n\prod_{p_i|n}(1-\frac{1}{p_i}) \ (p_i \in prime) \]
维护\(n\)可以直接用维护区间乘法的线段树,由于\(n\)与质因子可以分开维护,此时就可以直接取模了

接下来的问题是维护质因子

我们惊喜的发现\(x\leq300\)!这说明最多可能只有\(62\)个质因子

一个朴素的想法是,对每个质因子开一颗线段树

每次查询\([L,R]\)这个区间中是否有该质因子

考虑进行优化,我们发现\(62\)\(\tt{long \ long}\)二进制下的范围很相近

我们进行压位,把\(62\)个质因子压成一个\(\tt{long \ long }\)

这样\(62\)颗线段树就被我们压成一颗线段树了,维护区间或即可


代码

#include <cstdio>

using namespace std;

const int N = 1e6 + 10;
const int mod = 998244353;

int n, m;
int id[N], pri[N];

long long inv[N];

long long qpow(long long x, long long y) {
    long long res = 1;
    while (y) {
        if (y & 1)  res = res * x % mod;
        x = x * x % mod, y >>= 1;
    }
    return res;
}

struct tree_mul {
#define ls x << 1
#define rs x << 1 | 1

    long long val[N << 2], tag[N << 2];

    void clear() { for (int i = 1; i <= (n << 2); ++i)    tag[i] = val[i] = 1; }

    void pushdown(int x, int l, int r) {
        int mid = l + r >> 1;
        if (tag[x] != 1) {
            (tag[ls] *= tag[x]) %= mod, (tag[rs] *= tag[x]) %= mod;
            (val[ls] *= qpow(tag[x], mid - l + 1)) %= mod, (val[rs] *= qpow(tag[x], r - mid)) %= mod;
            tag[x] = 1;
        }
    }

    void modify(int x, int l, int r, int ql, int qr, long long v) {
        if (ql <= l && r <= qr) {
            (val[x] *= qpow(v, r - l + 1)) %= mod, (tag[x] *= v) %= mod;
            return;
        }
        int mid = l + r >> 1;
        pushdown(x, l, r);
        if (ql <= mid)
            modify(ls, l, mid, ql, qr, v);
        if (qr > mid)
            modify(rs, mid + 1, r, ql, qr, v);
        val[x] = val[ls] * val[rs] % mod;
    }    

    long long query(int x, int l, int r, int ql, int qr) {
        if (ql <= l && r <= qr)
            return val[x];
        int mid = l + r >> 1;
        long long res = 1;
        pushdown(x, l, r);
        if (ql <= mid)
            (res *= query(ls, l, mid, ql, qr)) %= mod;
        if (qr > mid)
            (res *= query(rs, mid + 1, r, ql, qr)) %= mod;
        return res;
    }

#undef ls
#undef rs
} a;

struct tree_or {
#define ls x << 1
#define rs x << 1 | 1

    long long val[N << 2], tag[N << 2];

    void clear() { for (int i = 1; i <= (n << 2); ++i) val[i] = tag[i] = 0; }

    void pushdown(int x) {
        if (tag[x]) {
            tag[ls] |= tag[x], tag[rs] |= tag[x];
            val[ls] |= tag[x], val[rs] |= tag[x];
            tag[x] = 0;
        }
    }

    void modify(int x, int l, int r, int ql, int qr, long long v) {
        if (ql <= l && r <= qr) {
            val[x] |= v, tag[x] |= v;
            return;
        }
        int mid = l + r >> 1;
        pushdown(x);
        if (ql <= mid)
            modify(ls, l, mid, ql, qr, v);
        if (qr > mid)
            modify(rs, mid + 1, r, ql, qr, v);
        val[x] = val[ls] | val[rs];
    }

    long long query(int x, int l, int r, int ql, int qr) {
        if (ql <= l && r <= qr) 
            return val[x];
        int mid = l + r >> 1;
        long long res = 0;
        pushdown(x);
        if (ql <= mid)
            res |= query(ls, l, mid, ql, qr);
        if (qr > mid)
            res |= query(rs, mid + 1, r, ql, qr);
        return res;
    }
#undef ls
#undef rs
} b;

long long divide(long long x) {
    long long res = 0;
    for (int i = 2; i <= x; ++i) {
        if (x % i)  continue;
        res |= (1LL << id[i]);
        while (x % i == 0)  x /= i;
    }
    return res;
}

void init() {
    int tot = 0;
    for (int i = 2; i <= 300; ++i) {
        int ok = 1;
        for (int j = 2; j * j <= i; ++j)
            if (i % j == 0) ok = 0;
        if (ok) id[i] = ++tot, pri[tot] = i;
    }
    for (int i = 1; i <= tot; ++i)
        inv[i] = qpow(pri[i], mod - 2);
    a.clear(), b.clear();
}

int main() {

    scanf("%d%d", &n, &m);

    init();

    long long x;
    for (int i = 1; i <= n; ++i) {
        scanf("%lld", &x);
        a.modify(1, 1, n, i, i, x);
        b.modify(1, 1, n, i, i, divide(x));
    }

    int op, l, r;
    while (m--) {
        scanf("%d", &op);
        if (op == 1) {
            scanf("%d%d%lld", &l, &r, &x);
            a.modify(1, 1, n, l, r, x);
            b.modify(1, 1, n, l, r, divide(x));
        }
        if (op == 2) {
            scanf("%d%d", &l, &r);
            long long num = a.query(1, 1, n, l, r);
            long long fac = b.query(1, 1, n, l, r);
            for (int i = 1; i <= 62; ++i)
                if (fac >> i & 1)   num = (num - num * inv[i] % mod + mod) % mod;
            printf("%lld\n", num);
        }
    }

    return 0;
}

9.9 另一个区间问题

标签:长度   tin   var   线段树   一个   ==   inline   序列   for   

原文地址:https://www.cnblogs.com/VeniVidiVici/p/11509272.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!