CodeForces 594D REQ(树状数组)

分析:第一反应是用莫队来写,这样每次查询的复杂度是 o(nn) ,设 M a[i]中最大的数,那么每一次修改的复杂度是 o(logM) ,那么总的复杂度是 o(nnlogM) ,这样的复杂度给人的感觉应该是要 T 了,交上去确实也T了,如果n 104 级别应该就没什么问题了。

感觉这个可以作为莫队算法在一类问题上的优化,因为每一次都是查询 [l,r] 这个区间所有不同的质数,最终的答案是

i=lra[i]p1p

看这个式子,前面一块我们可以维护前缀积,每次查询 o(1) 就可以搞定,后面一块要找出区间 [l,r] 中所有的不同的质数的函数的乘积,用莫队平均每次查找的复杂度是 o(n) ,这个地方能不能优化?
当固定左端点 l 的时候,把[l,n]这段区间所有的质数第一次出现的位置记录下来,把所有出现的质数扔到他们第一次出现的位置上面,用线段树维护的话,每一次查询的复杂度就是$o(logn)。

现在需要解决的是 ll+1 这个时候线段树维护的区间变成了 [l+1,n] ,即把 a[l] 的影响去掉,就是把线段是中 l 的位置的乘积变成1(这个地方我写的时候考虑复杂了),那么假如 p|a[l] 这个时候把 p 去掉了,[l+1,n]中仍然有 p 的倍数,这个时候要把右边第一个出现p的倍数的位置的中对应的线段树的位置乘上 p1p ,这题就做完了。

复杂度:预处理所有质因数,并且每个数所含有的质数 o(MlogM) ,扫一遍 [1,n] 中所有能整除的质数的位置 o(nlogM) ,树状数组查找 o(nlognlogM)

总的复杂度就是 o(MlogM)+o(nlognlogM)

代码:(自己写的比较挫,各种预处理,一开始还 MLE

#include <bits/stdc++.h>
#define LL long long
#define FOR(i,x,y)  for(int i = x;i < y;++ i)
#define IFOR(i,x,y) for(int i = x;i > y;-- i)

using namespace std;

typedef vector <int> VT;

const int Mod = 1e9+7;
const int maxn = 1000010;

map <int,int> mat;

int prime[maxn];
bool check[maxn];
VT pri[maxn];

void get_prime(){
    FOR(i,0,maxn)   pri[i].clear();
    memset(check,false,sizeof(check));
    prime[0] = 0;
    FOR(i,2,maxn){
        if(!check[i]){
            prime[++prime[0]] = i;
            pri[i].push_back(i);
            mat[i] = prime[0];
        }
        FOR(j,1,prime[0]+1){
            if(i*prime[j] >= maxn)  break;
            int u = i*prime[j];
            check[u] = true;
            FOR(k,0,(int)pri[i].size()) pri[u].push_back(pri[i][k]);
            if(i%prime[j] == 0) break;
            pri[u].push_back(prime[j]);
        }
    }
}

void gcd(LL a,LL b,LL& d,LL& x,LL& y){
    if(!b)  {d = a; x = 1; y = 0;}
    else {gcd(b,a%b,d,y,x); y -= x*(a/b);}
}

LL inv(LL a,LL n){
    LL d,x,y;
    gcd(a,n,d,x,y);
    return d == 1 ? (x+n)%n : -1;
}

LL mul[80000],imul[80000];

void init(){
    get_prime();
    FOR(i,1,prime[0]+1){
        mul[i] = (prime[i]-1)*inv(prime[i],Mod)%Mod;
        imul[i] = prime[i]*inv(prime[i]-1,Mod)%Mod;
    }
}

const int maxm = 200020;

struct Commends{
    int l,r,id;
    bool operator < (const Commends& rhs) const{
        return l < rhs.l;
    }
}cmd[maxm];

const int __cntpri = 80000;

queue <int> r_mx[__cntpri];

int n,a[maxm],b[maxm],q;
LL c[maxm],res[maxm],sum[maxm],isum[maxm];

int lowbit(int x)   {return x&-x;}

void modify(int x,LL val){
    while(x <= n){
        c[x] *= val;
        c[x] %= Mod;
        x += lowbit(x);
    }
}

LL query(int x){
    LL ans = 1;
    while(x){
        ans *= c[x];
        ans %= Mod;
        x -= lowbit(x);
    }
    return ans;
}

void work(){
    FOR(i,1,n+1)    c[i] = 1;
    FOR(i,1,prime[0]+1){
        if(!r_mx[i].empty()) modify(r_mx[i].front(),mul[i]);
    }
    int l = 1;
    FOR(i,0,q){
        res[cmd[i].id] = sum[cmd[i].r]*isum[cmd[i].l-1]%Mod;
        while(l<cmd[i].l){
            FOR(j,0,(int)pri[a[l]].size()){
                int u = pri[a[l]][j];
                modify(r_mx[mat[u]].front(),imul[mat[u]]);
                r_mx[mat[u]].pop();
                if(!r_mx[mat[u]].empty()){
                    modify(r_mx[mat[u]].front(),mul[mat[u]]);
                }
            }
            l++;
        }
        LL tem = query(cmd[i].r);
        res[cmd[i].id] = res[cmd[i].id]*tem%Mod;
    }
    FOR(i,0,q){
        printf("%I64d\n",res[i]);
    }
}

int main()
{
    //freopen("test.in","r",stdin);
    init();
    while(~scanf("%d",&n)){
        sum[0] = isum[0] = 1;
        FOR(i,1,n+1)    scanf("%d",&a[i]),sum[i] = sum[i-1]*a[i]%Mod,isum[i] = isum[i-1]*inv(a[i],Mod)%Mod;
        scanf("%d",&q);
        FOR(i,0,q)  scanf("%d%d",&cmd[i].l,&cmd[i].r),cmd[i].id = i;
        sort(cmd,cmd+q);
        FOR(i,1,prime[0]+1)   while(!r_mx[i].empty()) r_mx[i].pop();
        FOR(i,1,n+1){
            FOR(j,0,(int)pri[a[i]].size())  r_mx[mat[pri[a[i]][j]]].push(i);
        }
        work();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值