CF1114F Please, another Queries on Array?【线段树+欧拉函数】

题意

你有一个数组 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,,an

现在你需要完成 q q q次操作,有以下两种操作形式:

  1. MULTIPLY l r x,对于所有 i ( l ≤ i ≤ r ) i(l\le i\le r) i(lir),将 a i a_i ai乘上 x x x

  2. TOTIENT l r,求出 φ ( ∏ i = l r a i ) \varphi(\prod_{i=l}^ra_i) φ(i=lrai),对 1 0 9 + 7 10^9+7 109+7取模后的结果。其中 φ \varphi φ表示欧拉函数, φ ( n ) \varphi(n) φ(n)的定义为 1 … n 1\dots n 1n中与 n n n互质的数的个数。

时间限制

5.50s

内存限制

250.00MB

思路

对于某个数的欧拉函数,可以通过质因数分解求解,若 a = ∑ i p i α i a = \sum_ip_i^{\alpha_i} a=ipiαi,那么 φ ( a ) = a ∏ i p i − 1 p i \varphi(a) = a\prod_i\frac{p_i - 1}{p_i} φ(a)=aipipi1
同时,观察到每个数的大小只有300,通过猜想与打表验证之后发现300以内的质数只有62个,那么可以把是否包含这些素数用 l o n g   l o n g long\ long long long状压起来。用线段树维护每一个区间所有数的乘积极其包含的质因数即可。
维护每个区间数的乘积就是维护乘法而已,非常简单。
维护每个区间数的乘积包含的质因数,由于状压起来了,那么两个子区间的状态进行或运算就可以。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <cmath>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 400005, mod = 1000000007;

int n, m;
int a[N];
int p[305];
bool vis[305];

struct Tree
{
    int l, r;
    LL pstate; int mul;
    LL plz; int mullz;
}t[N << 2];

void get_prime(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (!vis[i]) p[++ p[0]] = i;
        for (int j = 1; j <= p[0] && i * p[j] <= n; j ++ )
        {
            vis[i * p[j]] = true;
            if (i % p[j] == 0) break;
        }
    }
}

int qpow(int a, int b)
{
    int res = 1;
    while (b)
    {
        if (b & 1) res = 1LL * res * a % mod;
        b >>= 1;
        a = 1LL * a * a % mod;
    }
    return res;
}

void push_up(int i)
{
    t[i].mul = 1LL * t[i << 1].mul * t[i << 1 | 1].mul % mod;
    t[i].pstate = t[i << 1].pstate | t[i << 1 | 1].pstate;
}

void calc(int i, int mul, LL state)
{
    t[i].mul = 1LL * t[i].mul * qpow(mul, t[i].r - t[i].l + 1) % mod;
    t[i].pstate |= state;
    t[i].mullz = 1LL * t[i].mullz * mul % mod;
    t[i].plz |= state;
}

void push_down(int i)
{
    if (!t[i].plz) return;
    int mul = t[i].mullz;
    LL state = t[i].plz;
    calc(i << 1, mul, state);
    calc(i << 1 | 1, mul, state);
    t[i].plz = 0, t[i].mullz = 1;    
}

void build(int i, int l, int r)
{
    t[i].l = l, t[i].r = r;
    t[i].mul = 1, t[i].mullz = 1;
    if (l == r)
    {
        int x = a[l];
        t[i].mul = x;
        for (int j = 1; j <= p[0]; j ++ )
            if (x % p[j] == 0)
                t[i].pstate |= 1LL << (j - 1);
        return;
    }
    int mid = (l + r) >> 1;
    build(i << 1, l, mid);
    build(i << 1 | 1, mid + 1, r);
    push_up(i);
}

void update(int i, int l, int r, int x, LL xp)
{
    if (l <= t[i].l && t[i].r <= r)
    {
        calc(i, x, xp);
        return;
    }
    push_down(i);
    int mid = (t[i].l + t[i].r) >> 1;
    if (l <= mid) update(i << 1, l, r, x, xp);
    if (r > mid) update(i << 1 | 1, l, r, x, xp);
    push_up(i);
}

pair<LL, int> query(int i, int l, int r)
{
    if (l <= t[i].l && t[i].r <= r)
    {
        pair<LL, int> tt = make_pair(t[i].pstate, t[i].mul);
        return tt;
    }
    push_down(i);
    int mid = (t[i].l + t[i].r) >> 1;
    if (l > mid) return query(i << 1 | 1, l, r);
    else if (r <= mid) return query(i << 1, l, r);
    else 
    {
        pair<LL, int> ls = query(i << 1, l, r);
        pair<LL, int> rs = query(i << 1 | 1, l, r);
        ls.first |= rs.first;
        ls.second = 1LL * ls.second * rs.second % mod;
        return ls;
    }
}

int main()
{
    #ifdef ZYCMH
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    #endif
    get_prime(300);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ )
        scanf("%d", &a[i]);

    build(1, 1, n);
    
    for (int i = 1; i <= m; i ++ )
    {
        char s[10];
        scanf("%s", s);
        if (s[0] == 'T') 
        {
            int l, r;
            scanf("%d%d", &l, &r);
            pair<LL, int> t = query(1, l, r);
            int ans = t.second;
            LL pstate = t.first;
            for (int j = 1; j <= p[0]; j ++ )
                if (pstate & (1LL << (j - 1)))
                    ans = 1LL * ans * (p[j] - 1) % mod * qpow(p[j], mod - 2) % mod;
            printf("%d\n", ans);
        }
        else 
        {
            int l, r, x;
            scanf("%d%d%d", &l, &r, &x);
            LL xp = 0;
            for (int j = 1; j <= p[0]; j ++ )
                if (x % p[j] == 0) 
                    xp += (1LL << (j - 1));
            update(1, l, r, x, xp);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值