CF 1117F

给定一个数列,两种操作,

1 区间 乘 x  

2 求区间 累乘 的欧拉函数 对 1e9+1 取模
我们发现线段树可以轻松搞定,可是 n 4e5 虽然 ai 只有 300,直接暴力肯定超 ll ,

首先区间 乘法 可以要线段树维护,而欧拉函数我们知道 = n* (p1-1)/p1 *(p2-1)/p2。。。

那么就可以逆元,而且 300以内素数有 62个,我们可以一一记录下来对于每个数,我们都先进行一次预处理,记录它的全部素因数,但是如果线段树每个点都开 62个 vis 会爆 memory ,因为只有 62个数,所以完全可以用一个 long long 状压掉,那么预处理就很简单了,之后只需要 区间维护的时候 其一是把数字相乘, 其二要把二者之间的状态进行一下 或 操作,所以不如用 pair

之后算出区间和之后,直接遍历这 62 个素数,出现过的就 代入一下欧拉公式。。。

 

#include<bits/stdc++.h>
using namespace std;
#define ll long long int
#define ls rt<<1
#define rs rt<<1|1
#define root 1,1,n
#define pr pair<ll, ll>
#define mpr make_pair
#define fi first
#define se second
#define pb(a) push_back(a)
const int maxn=4e5+5;
const int mod=1e9+7;
inline ll lread()
{
    register ll s=0,w=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
inline int read()
{
    register int s=0,w=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
ll qpow(ll a,ll n)
{
    ll num = 1;
    while(n)
    {
        if(n&1) num=num*a%mod;
        a = a*a%mod;
        n >>= 1;
    }
    return num;
}
vector<ll>v;
ll mark[305];
pr mul(pr a, pr b){return mpr(a.fi*b.fi%mod, a.se|b.se);}
inline void init()
{
    register bool vis[305];
    register int cnt = 0;
    memset(vis, false, sizeof vis);
    memset(mark, 0, sizeof mark);
    for(int i=2;i<=300;i++)
    {
        if(vis[i])continue;
        v.pb(i);
        for(int j=i;j<=300;j+=i)
        {
            vis[j]=true;
            mark[j]|=1LL<<cnt;
        }
        cnt ++;
    }
}
struct node
{
    int l,r;
    pr num; pr laz;
}a[maxn<<2];
inline void build(int rt,int l,int r)
{
    a[rt].l=l, a[rt].r=r;
    a[rt].laz={1,0};
    if(l == r)
    {
        int x; scanf("%d",&x);
        a[rt].num={x, mark[x]};
        return;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    a[rt].num = mul(a[ls].num, a[rs].num);
}
inline void update(int rt,int x,int y,pr k)
{
    if(x==a[rt].l && y==a[rt].r)
    {
        a[rt].laz = mul(a[rt].laz, k);
        k.fi = qpow(k.fi, y-x+1);
        a[rt].num = mul(a[rt].num, k);
        return;
    }
    if(a[rt].laz.fi!=1 || a[rt].laz.se!=0)
    {
        update(ls, a[ls].l, a[ls].r, a[rt].laz);
        update(rs, a[rs].l, a[rs].r, a[rt].laz);
        a[rt].laz = {1,0};
    }
    if(y<=a[ls].r)update(ls, x, y, k);
    else if(x>=a[rs].l)update(rs, x, y, k);
    else
    {
        update(ls, x, a[ls].r, k);
        update(rs, a[rs].l, y, k);
    }
    a[rt].num = mul(a[ls].num, a[rs].num);
}
pr query(int rt,int x,int y)
{
    if(x==a[rt].l && y==a[rt].r) return a[rt].num;
    if(a[rt].laz.fi!=1 || a[rt].laz.se!=0)
    {
        update(ls, a[ls].l, a[ls].r, a[rt].laz);
        update(rs, a[rs].l, a[rs].r, a[rt].laz);
        a[rt].laz = {1,0};
    }
    if(y<=a[ls].r)return query(ls, x, y);
    else if(x>=a[rs].l)return query(rs, x, y);
    return mul(query(ls, x, a[ls].r), query(rs, a[rs].l, y));
}
int main()
{
    init();
    ll n=lread(),q=lread();
    build(1,1,n);
    while(q --)
    {
        char str[10]; int L,R,x;
        scanf("%s",str);
        L=read(),R=read();
        if(str[0]=='M')
        {
            x=read();
            update(1, L, R, mpr(x, mark[x]));
        }
        else
        {
            pr num = query(1, L, R);
            for(int i=0;i<62;i++)
            {
                if( (1LL<<i) & num.se )
                {
                    num.fi = num.fi*qpow(v[i], mod-2)%mod;
                    num.fi = num.fi*(v[i]-1)%mod;
                }
            }
            printf("%lld\n",num.fi);
        }
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值