【CodeForces - 1114F】:Please, another Queries on Array?【线段树+数论】

题面:

CodeForces - 1114F: Please, another Queries on Array?

题意:

给定一个数组,Q次操作,(1)【L,R】区间乘X,(2)询问区间乘积的欧拉函数

分析:

X和原数组的值都小于等于300,而300以内的质数只有62个,似乎可以在线段树上每个节点开一个大小为62的数组保存当前区间每个质数的出现次数,快速幂求欧拉函数就好了,N又大了点,要开将近1e8的数组,肯定是被卡了的;注意到 phi(x) = x * (p1-1)/p1 * (p2-1)/p2 * ... *(pn-1)/pn,所以只需要保存区间中所有数的乘积和每个质数出现的情况即可,62个素数刚好状压一下

代码:

#include <bits/stdc++.h>
 
using namespace std;
typedef long long LL;
const int mod = 1e9+7;
const int maxn = 4e5+45;
inline int read()
{
    int x = 0, f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
    return x * f;
}
inline void write(int x)
{
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
int n,q,l,r,x,a[maxn],id[320],val[320];
char op[20];
LL sz[maxn<<2],sb[maxn<<2],lz[maxn<<2],lb[maxn<<2],bit[320],inv[320];
/*
   sz[]保存区间乘积  sb[]区间乘积包含的素数
   lz[]懒惰标记区间乘 lb[]懒惰标记包含的素数
   bit[]每个数包含素数的状压
   inv[]预处理逆元
*/
void init()
{
    int cnt = 0;
    for(int i = 2; i <= 300; ++i){
        bool flag = true;
        for(int j = 2; j*j <= i; ++j){
            if(i % j == 0) flag = false;
        }
        if(flag) id[i] = cnt++,val[cnt-1] = i;
    }
    for(int i = 2;i <= 300; ++i){     
        int x = i;
        for(int j = 2;j*j<=x; ++j){
            if(x % j == 0){
                bit[i] |= (1ll<<id[j]);
                while(x % j == 0) x /= j;
            }
        }
        if(x > 1) bit[i] |= (1ll<<id[x]);
    }
    inv[1] = 1;                         //预处理逆元
    for(int i = 2;i <= 300; ++i) inv[i] = (mod-mod/i)*inv[mod%i]%mod;
}
inline LL qpow(LL a,LL x)
{
    LL res = 1ll; a %= mod;
    while(x){
        if(x&1) res = res * a % mod;
        a = a * a % mod;
        x >>= 1;
    }
    return res;
}
inline void pushup(int x){
    sz[x] = sz[x<<1]*sz[x<<1|1]%mod;
    sb[x] = sb[x<<1]|sb[x<<1|1];
}
inline void pushdown(int x,int l,int r){
    int mid = (l+r)>>1;
    sz[x<<1] = sz[x<<1]*qpow(lz[x],mid-l+1)%mod;
    sb[x<<1] |= lb[x];
    sz[x<<1|1] = sz[x<<1|1]*qpow(lz[x],r-mid)%mod;
    sb[x<<1|1] |= lb[x];
    lz[x<<1] = lz[x<<1]*lz[x]%mod;
    lb[x<<1] |= lb[x];
    lz[x<<1|1] = lz[x<<1|1]*lz[x]%mod;
    lb[x<<1|1] |= lb[x];
    lz[x] = 1; lb[x] = 0;
}
void build(int l,int r,int x)
{
    lz[x] = 1; 
    if(l == r){
     sz[x] = a[l];
     sb[x] = bit[a[l]];
     return ;
    }
    int mid = (l+r) >> 1;
    build(l,mid,x<<1);
    build(mid+1,r,x<<1|1);
    pushup(x);
}
void updata(int l,int r,int L,int R,int x,int v)
{
    if(l > R || r < L) return ;
    if(l >= L && r <= R){
        sz[x] = sz[x] * qpow(v,r-l+1) % mod;
        sb[x] |= bit[v];
        lz[x] = lz[x] * v % mod;
        lb[x] |= bit[v]; 
        return ;      
    }
    if(lb[x]) pushdown(x,l,r);
    int mid = (l+r) >> 1;
    updata(l,mid,L,R,x<<1,v);
    updata(mid+1,r,L,R,x<<1|1,v);
    pushup(x);
}
void query(int l,int r,int L,int R,int x,LL &res,LL &t)
{
    if(l > R || r < L) return ;
    if(l >= L && r <= R){
        res = res * sz[x] % mod;
        t |= sb[x];
        return ;
    }
    if(lb[x]) pushdown(x,l,r);
    int mid = (l+r) >> 1;
    query(l,mid,L,R,x<<1,res,t);
    query(mid+1,r,L,R,x<<1|1,res,t);
}
int main()
{
    n = read(), q = read();
    for(int i = 1; i <= n; ++i) a[i] = read();
    init(); build(1,n,1);
    while(q--)
    {
        scanf("%s",op);
        if(op[0] == 'M'){
            l = read(),r = read(),x = read();
            updata(1,n,l,r,1,x);
        }
        else
        {
            l = read(),r = read();
            LL res = 1,t = 0,p = 1;
            query(1,n,l,r,1,res,t);
            for(int i = 62; ~i; --i){
                if(t&(1ll<<i)) p = p*(val[i]-1)%mod*inv[val[i]]%mod;
            }
            write(res*p%mod); putchar('\n');
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值