9.9 另一个区间问题

Posted venividivici

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9.9 另一个区间问题相关的知识,希望对你有一定的参考价值。

题意

有一个长度为\(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-\frac1p_i) \ (p_i \in prime) \]
维护\(n\)可以直接用维护区间乘法的线段树,由于\(n\)与质因子可以分开维护,此时就可以直接取模了

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

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

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

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

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

我们进行压位,把\(62\)个质因子压成一个\(\ttlong \ 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 另一个区间问题的主要内容,如果未能解决你的问题,请参考以下文章

c# 怎么判断一个时间区间在另一个时间区间?

EXCEL中如何判断一个时间区间是不是在另一个时间区间内?

P5199 [USACO19JAN]Mountain View S(区间&排序)

CF319E Ping-Pong

JS显示时间问题,一打开页面显示的时间区间是30天的,怎么修改成15天的

《入门经典》——6.28