[ZOJ]ZOJ3998(线段树,费马小定理)

题意:询问数列中一段的乘积,支持区间乘法和区间乘方

基本类似于支持区间加法和区间乘法的区间求和的线段树,对两种操作打两个tag,根据运算规则pushdown维护好标记即可。

乘方的取模利用费马小定理的推论:a^n ≡ a^ (n mod φ(p))(modp),p为质数时φ(p)=p-1

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,n) for(int i=1;i<=n;i++)

const int N=1e5+7;
const int mod=1e9+7;

int n,a[N],tree[N*4],tag1[N*4],tag2[N*4],len[N*4];

inline void update(int node){
    tree[node]=1LL*tree[node<<1]*tree[node<<1|1]%mod;
}

inline int po(int x,int y){
    int res=1;
      while(y){
      if(y&1)res=1LL*res*x%mod;
      y>>=1;
      x=1LL*x*x%mod;
      }
      return res;
}

inline void maketag(int node,int v,int k){
    tree[node]=1LL*po(tree[node],k)*po(v,len[node])%mod;
    tag1[node]=1LL*po(tag1[node],k)*v%mod;
    tag2[node]=1LL*tag2[node]*k%(mod-1);
}

inline void push_down(int node){
    maketag(node<<1,tag1[node],tag2[node]);
    maketag(node<<1|1,tag1[node],tag2[node]);
    tag1[node]=tag2[node]=1;
}

inline int query(int node,int L,int R,int l,int r){
    if(l>R||r<L)return 1; 
    if(L>=l&&R<=r)return tree[node];
    push_down(node);
    int mid=(L+R)>>1;
    return 1LL*query(node<<1,L,mid,l,r)*query(node<<1|1,mid+1,R,l,r)%mod;
}

inline void change(int node,int L,int R,int l,int r,int v,int k){
    if(l>R||r<L)return;
    if(L>=l&&R<=r){
        maketag(node,v,k);
        return ;
    }
    if(tag1[node]!=1||tag2[node]!=1)push_down(node);
    int mid=(L+R)>>1;
    change(node<<1,L,mid,l,r,v,k);
    change(node<<1|1,mid+1,R,l,r,v,k);
    update(node);
}

void build(int node,int l,int r){
    tag1[node]=tag2[node]=1;
    len[node]=r-l+1;
    if(l==r){
        tree[node]=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(node<<1,l,mid);
    build(node<<1|1,mid+1,r);
    update(node);
}

int main()
{
    int t,q;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&q);

        rep(i,n)scanf("%d",&a[i]);

        build(1,1,n);

        while(q--){
            int op,l,r,v;
            scanf("%d%d%d",&op,&l,&r);
            if(op==1){
                scanf("%d",&v);
                change(1,1,n,l,r,v,1);
            }
            if(op==2){
                scanf("%d",&v);
                change(1,1,n,l,r,1,v);
            }
            if(op==3){
                printf("%d\n",query(1,1,n,l,r));
            }
        }
    }
    //system("pause");
}

 

posted on 2019-03-25 00:49  枫棠  阅读(226)  评论(0编辑  收藏  举报

导航