1. 程式人生 > >【51nod1642】區間歐拉函數

【51nod1642】區間歐拉函數

print turn pre n) tmp -- long long max pow(x

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]);
}

【51nod1642】區間歐拉函數