【51nod1642】区间欧拉函数

Description

求区间\([l,r]\)权值积的欧拉函数值。
详细题面

Solution

直接考虑一个数的欧拉函数如何计算,有:\(φ(x)=x\prod \dfrac{p-1}{p}\)\(p\)\(x\)的质因子)
要求一段区间权值积欧拉函数值其实就是要求这个区间包含不同质数的\(\dfrac{p-1}{p}\)的积乘以区间的积。

考虑离线,把询问按照\(r\)排序,区间不同质数乘积可以用树状数组维护。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
typedef long long ll;
const int N=2e5+10,mo=1e9+7;
struct node{
    int l,r,p;
}b[N];
int a[N],n;
ll s[N],tr[N];
int pos[N*5],an[N];
int pr[N],mp[N*5];
bool bz[N*5];
int mx=0;
void mul(int x,int t){
    for(;x<=n;x+=x&-x) tr[x]=tr[x]*t%mo;
}
ll calc(ll x){
    ll tmp=1;
    for(;x;x-=x&-x) tmp=tmp*tr[x]%mo;
    return tmp;
}
ll pow(ll x,int y){
    ll t=1;
    while(y){
        if(y&1) t=t*x%mo;
        y>>=1,x=x*x%mo;
    }
    return t;
}
bool cmp(node x,node y){
    return x.r<y.r;
}
void pre(){
    fo(i,2,mx){
        if(!bz[i]) pr[++pr[0]]=i,bz[i]=1,mp[i]=i;
        fo(j,1,pr[0]){
            int t=i*pr[j];
            if(t>mx) break;
            bz[t]=1,mp[t]=min(mp[i],pr[j]);
            if(i%pr[j]==0) break;
        }
    }
}
void add(int p){
    int x=a[p];
    /*for(int i=2;i*i<=x;i++)
    if(x%i==0){
        while(x%i==0) x/=i;
        ll t=(ll)(i-1)*pow(i,mo-2)%mo;
        if(pos[i]) mul(pos[i],pow(t,mo-2));
        pos[i]=p,mul(p,t);
    }
    if(x>1){
        ll t=(ll)(x-1)*pow(x,mo-2)%mo;
        if(pos[x]) mul(pos[x],pow(t,mo-2));
        pos[x]=p,mul(p,t);
    }*/
    while(x>1){
        int i=mp[x];
        while(x%i==0) x/=i;
        ll t=(ll)(i-1)*pow(i,mo-2)%mo;
        if(pos[i]) mul(pos[i],pow(t,mo-2));
        pos[i]=p,mul(p,t);
    }
}
int main()
{
    scanf("%d",&n);
    s[0]=1;
    fo(i,1,n){
        scanf("%d",&a[i]),s[i]=s[i-1]*a[i]%mo;
        mx=max(mx,a[i]),tr[i]=1;
    }
    pre();
    int q;
    scanf("%d",&q);
    fo(i,1,q) scanf("%d %d",&b[i].l,&b[i].r),b[i].p=i;
    sort(b+1,b+q+1,cmp);
    int p=0;
    fo(i,1,n){
        add(i);
        while(p<q && b[p+1].r==i){
            p++;
            int wz=b[p].p;
            an[wz]=calc(b[p].r)*pow(calc(b[p].l-1),mo-2)%mo;
            an[wz]=an[wz]*s[b[p].r]%mo*pow(s[b[p].l-1],mo-2)%mo;
        }
    }
    fo(i,1,q) printf("%lld\n",an[i]);
}

转载于:https://www.cnblogs.com/sadstone/p/9048642.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值