[JZOJ5595]欧拉函数

题目描述

对于正整数n,定义欧拉函数φ(n) 为小于等于n 且与n 互质的正整数个数。例如
φ(1) = 1,φ(8) = 4。
给定正整数序列a1; a2;…; an,请依次执行q 个操作,操作有以下三种类型:
• 0 i x:修改a[i] 的值为x;
• 1 l r:查询φ(a[l] + a[l+1] + … + a[r]) 的值,输出这个值对10^9 + 7 取模的结果;
• 2 l r:查询φ(a[l] * a[l+1] * … * a[r]) 的值,输出这个值对10^9 + 7 取模的结果。
这里写图片描述

解题思路

只考虑操作01,数据结构求和,暴力求phi即可。
单次最大复杂度 O(2e9ln2e9) O ( 2 e 9 l n 2 e 9 ) ,2000左右,而且随机情况下复杂度肯定不满,开O2肯定能过。
不放心打millerrabin也可以…虽然最后复杂度估计400左右,不过常数也很大。
考虑操作2怎么做。
原式相当于 (iai)pp1p ( ∏ i a i ) ∗ ∏ p p − 1 p ,其中p枚举的是出现过的质因子。
前面部分用线段树顺便维护一下就行了,而后面部分则是经典的统计带权颜色数做法。
考虑我们需要统计带权等价类,即带权颜色数,我们考虑只统计每个颜色块的最左边被询问区间[l,r]包含的那个点,我们需要设计计算方法。
具体的,设这个点位置为y,他的前一个位置为x,我们考虑在怎样的[l,r]的区间内,他会被当成关键点。发现这样的区间满足 x<l<=y<=r x < l <= y <= r 。那么转化成对于一个给定的区间[l,r],我们想统计所有满足这个条件的点对的贡献。发现可以变成二维区间查询问题,那么就是树套树或者KD树之类的维护一下区间乘积的经典操作了。
那么对于一个数,把每个质因子当成一种颜色,然后用set之类的维护每种颜色位置,每次修改就改一些点对即可,质因子的数量可以看成常数,那么时间复杂度是 O(nlog2n) O ( n l o g 2 n ) 的·

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
const int N=1e5+5,M=5e6+5,mo=1e9+7;
multiset<int> col[N];
multiset<int> :: iterator it;
int pri[N],fac[N],rev[N],tr[2][N*4],n,m,i,a[N],l,ka,x,y,j,ref[N],ans,L,R,S,tmp,val,pos,d[N],z,v,ri,le;
int rt[N],vtr[M*3],ls[M*3],rs[M*3],ts;
bool pd[N];
int read()
{
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
int ksm(int x,int y)
{
    int ret=1;
    while (y)
    {
        if (y&1) ret=1ll*ret*x%mo;
        y>>=1;
        x=1ll*x*x%mo;
    }
    return ret;
}
void Predo(int n)
{
    int i,t,j;
    fo(i,2,n)
    {
        if (!pd[i])
        {
            pri[++pri[0]]=i;
            fac[i]=i;
            if (i<=40000) ref[i]=pri[0];
        }
        fo(j,1,pri[0])
        {
            if (1ll*i*pri[j]>n) break;
            t=i*pri[j];
            pd[t]=1;
            fac[t]=pri[j];
            if (i%pri[j]==0)
                break;
        }
    }
    fo(i,1,40000) rev[i]=ksm(i,mo-2);
}
void change(int x,int l,int r)
{
    if (l==r) {tr[0][x]=tr[1][x]=val;return;}
    int m=l+r>>1;
    if (pos<=m) change(x*2,l,m);
    else change(x*2+1,m+1,r);
    tr[0][x]=tr[0][x*2]+tr[0][x*2+1];
    tr[1][x]=1ll*tr[1][x*2]*tr[1][x*2+1]%mo;
}
void calc(int x)
{
    if (!S) ans+=x;
    else ans=1ll*ans*x%mo;
}
void get(int x,int l,int r)
{
    if (L<=l&&r<=R) {calc(tr[S][x]);return;}
    int m=l+r>>1;
    if (L<=m) get(x*2,l,m);
    if (m<R) get(x*2+1,m+1,r);
}
int phi(int n)
{
    int ret=1;
    fo(i,1,pri[0])
    {
        if (pri[i]*pri[i]>n) break;
        if (n%pri[i]==0)
        {
            n/=pri[i];
            ret=ret*(pri[i]-1)%mo;
            while (n%pri[i]==0)
            {
                n/=pri[i];
                ret=ret*pri[i]%mo;
            }
        }
    }
    if (n>1) ret=ret*(n-1);
    return ret%mo;
}
void get(int n)
{
    int x;
    d[0]=0;
    while (n>1)
    {
        x=fac[n];
        while (x==fac[n/x])
            n/=x;
        n/=x;
        d[++d[0]]=x;
    }
}
void ins(int &x,int l,int r,int p,int v)
{
    if (!x) x=++ts,vtr[x]=1;
    if (l==r) {vtr[x]=1ll*vtr[x]*v%mo;return ;}
    int m=l+r>>1;
    if (p<=m) ins(ls[x],l,m,p,v);
    else ins(rs[x],m+1,r,p,v);
    vtr[x]=1ll*vtr[ls[x]]*vtr[rs[x]]%mo;
}
void get(int x,int l,int r,int i,int j)
{
    if (!x) return;
    if (l==i&&r==j)
        {tmp=1ll*tmp*vtr[x]%mo;return;}
    int m=l+r>>1;   
    if (i<=m) get(ls[x],l,m,i,min(j,m));
    if (m<j) get(rs[x],m+1,r,max(m+1,i),j);
}
void ins(int x,int y,int val)// constant added
{
    x++;
    while (x<n+1)
    {
        ins(rt[x],1,n,y,val);
        x+=x&(-x);
    }
}
void get(int lx,int rx,int ly,int ry)
{
    int x;
    x=rx+1;
    while (x)
    {
        get(rt[x],1,n,ly,ry);
        x-=x&(-x);
    }
}
int main()
{
    freopen("t2.in","r",stdin);
    freopen("t2.out","w",stdout);
    Predo(4e4);
    scanf("%d %d",&n,&m);
    fo(i,1,40000) col[i].insert(0);
    vtr[0]=1;
    fo(i,1,n) 
    {
        a[i]=read();
        pos=i;val=a[i];
        change(1,1,n);
        get(a[i]);
        fo(j,1,d[0])
        {
            it=col[d[j]].end();
            ins(*(--it),i,1ll*(d[j]-1)*rev[d[j]]%mo);
            col[d[j]].insert(i);
        }
    }
    fo(l,1,m)
    {
        ka=read();x=read();y=read();
        if (!ka)
        {
            pos=x;val=y;
            change(1,1,n);
            get(a[x]);
            fo(i,1,d[0])
            {
                z=d[i];
                le=ri=0;
                it=col[z].find(x);
                col[z].erase(it);
                it=col[z].lower_bound(x);
                v=1ll*z*rev[z-1]%mo;
                if (it!=col[z].end()) 
                    ins(x,ri=*it,v);
                ins(le=*(--it),x,v);
                if (ri) ins(le,ri,1ll*(z-1)*rev[z]%mo);
            }
            a[x]=y;
            get(a[x]);
            fo(i,1,d[0])
            {
                z=d[i];
                le=ri=0;
                it=col[z].lower_bound(x);
                v=1ll*(z-1)*rev[z]%mo;
                if (it!=col[z].end()) 
                    ins(x,ri=*it,v);
                ins(le=*(--it),x,v);
                if (ri) ins(le,ri,1ll*z*rev[z-1]%mo);
                col[z].insert(x);
            }
        }
        else if (ka==1)
        {
            L=x;R=y;ans=S=0;
            get(1,1,n);
            printf("%d\n",phi(ans));
        }
        else
        {
            L=x;R=y;tmp=ans=S=1;
            get(1,1,n);
            get(1,L-1,L,R);
            ans=1ll*ans*tmp%mo;
            printf("%d\n",ans);
        }
    }
    ans=0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值