• [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
    ",query(1,1,n,l,r));
                }
            }
        }
        //system("pause");
    }
  • 相关阅读:
    个人项目实战
    准备
    结队项目-第一次作业
    第三次作业--团队展示
    软件工程实践第二次作业
    软件工程实践第一次作业
    circle
    calculator
    视频课程学习及学习计划
    1001.A+B Format (20)
  • 原文地址:https://www.cnblogs.com/xutianshu/p/10591583.html
Copyright © 2020-2023  润新知