zoukankan      html  css  js  c++  java
  • Codeforces Round #538 (Div. 2) F 欧拉函数 + 区间修改线段树

    https://codeforces.com/contest/1114/problem/F
    欧拉函数 + 区间更新线段树

    题意

    对一个序列(n<=4e5,a[i]<=300)两种操作:
    1. 将a[l,r]的数字乘以x(x<=300)
    2. 求(varphi(prod_{i=l}^ra[i]))对1e9+7取模

    题解

    欧拉函数性质

    1. 假如(p)是一个质数,(varphi(p)=p-1),(varphi(p^k)=p^{k-1}*(p-1)=p^k*frac{p-1}{p})
    2. 假如p,q互质,(varphi(p*q)=varphi(p)*varphi(q))
    3. 对于一个正整数n,(varphi(n)=n*frac{p_1-1}{p_1}*...*frac{p_n-1}{p_n})
    • 对于每个数分开维护(n)(frac{p_1-1}{p_1}*...*frac{p_n-1}{p_n}),因为所有数只有300大,有62位素数,所以可以用位运算维护后半部分,剩下前半部分就是维护数的乘积

    处理

    • 开两个标记数组,ly[]维护乘积的延迟标记,st[]维护位运算的延迟标记
    • 每次区间操作找到合适的区间就直接修改,需要向下递归前才向下推标记
    • 预处理出x(x<=300)每个数的素因子bit[],可以在埃式筛的过程中处理
    • 查询的时候,需要用两个信息用array<ll,2>,
      附上重载加号代码
    array<ll,2> operator +(array<ll,2> a,array<ll,2> b){
        return {a[0]*b[0]%P,a[1]|b[1]};
    }
    

    代码(区间修改线段树板子)

    #include<bits/stdc++.h>
    #define P 1000000007
    #define ls (o<<1)
    #define rs (o<<1|1)
    #define ll long long 
    #define M 1600000
    using namespace std;
    ll x[M],s[M],ly[M],st[M];
    ll bit[305],pr[305],inv[305];
    int vi[305];
    ll a[400005],tp,X,cnt,i;
    int n,q,l,r;
    char S[20];
    array<ll,2>ans;
    array<ll,2> operator +(array<ll,2> a,array<ll,2> b){
        return {a[0]*b[0]%P,a[1]|b[1]};
    }
    ll pw(ll bs,ll x){
    	ll ans=1;while(x){if(x&1)ans=ans*bs%P;bs=bs*bs%P;x>>=1;}
    	return ans;
    }
    void push_up(int o){                     //st数组和ly数组同理,作用为标记每次改变量
        x[o]=x[ls]*x[rs]%P;
        s[o]=s[ls]|s[rs];
        ly[o]=1;
        st[o]=0;
    }
    void push_down(int o,int l,int r){        //更新子节点信息,将本节点标记去掉
        int mid=(l+r)/2;
        if(ly[o]>1){
            ly[ls]=ly[ls]*ly[o]%P;
            ly[rs]=ly[rs]*ly[o]%P;
            s[ls]=s[ls]|st[o];
            st[ls]=st[ls]|st[o];
            s[rs]=s[rs]|st[o];
            st[rs]=st[rs]|st[o];
            x[ls]=x[ls]*pw(ly[o],mid-l+1)%P;
            x[rs]=x[rs]*pw(ly[o],r-mid)%P;
            ly[o]=1;
            st[o]=0;
        }
    }
        
    void build(int o,int l,int r){
    	if(l==r){
    		ly[o]=x[o]=a[l];
            st[o]=s[o]=bit[a[l]];
    		return;
    	}
    	int mid=(l+r)/2;
    	build(ls,l,mid);build(rs,mid+1,r);
    	push_up(o);
    }
    
    array<ll,2> qy(int o,int l,int r,int L,int R){
        int mid=(l+r)/2;    
    	array<ll,2>ans={1,0};
        if(L<=l&&r<=R){
            return {x[o],s[o]};
        } 
        push_down(o,l,r);
        if(L<=mid)ans=ans+qy(ls,l,mid,L,R);
        if(R>mid)ans=ans+qy(rs,mid+1,r,L,R);
        return ans;
    }
    
    void ud(int o,int l,int r,int L,int R,ll X){
        int mid=(l+r)/2;
    
        //if(l!=r)push_down(o,l,r);      //1.先要将上次的信息传下去不然就会清空了2.并且要特判是不是最后一层
        if(L<=l&&r<=R){
            ly[o]=X*ly[o]%P;x[o]=x[o]*pw(X,r-l+1)%P;
            st[o]|=bit[X];s[o]|=bit[X];
            return;
        }
    	push_down(o,l,r);                //只要不向下搜就不向下推
        if(L<=mid)ud(ls,l,mid,L,R,X);
        if(R>mid)ud(rs,mid+1,r,L,R,X);
        push_up(o);                      //更新了才需要向上推
    }
    
    void init(){
        for(int i=2;i<301;i++){
            if(!vi[i]){
                pr[++cnt]=i;
                inv[cnt]=pw(i,P-2);
                for(int j=i;j<301;j+=i){
                    vi[j]=1;bit[j]|=(1ll<<cnt);
                }
            }
        }
    }
    
    int main(){
        init();
    	cin>>n>>q;
    	for(i=1;i<=n;i++)scanf("%lld",&a[i]);
    	build(1,1,n);
    	while(q--){
    		scanf("%s",S);
    		if(S[0]=='M'){
    			scanf("%d%d%lld",&l,&r,&X);
    			ud(1,1,n,l,r,X);
    		}else{
    			scanf("%d%d",&l,&r);
                ans=qy(1,1,n,l,r);
                tp=ans[0]%P;
                for(i=1;i<63;i++){
                    if((ans[1]>>i)&1)tp=tp*(pr[i]-1)%P*inv[i]%P;
                }
                printf("%lld
    ",tp);
    		}
    	}
    }
    
  • 相关阅读:
    4-结对开发地铁
    第五周学习进度博客
    mybatis的使用
    从写json作业谈起
    工作中慢慢明白的道理
    从参与公司开发到离职
    这也是风云变幻的年代
    学习的习惯和方法跟得上时代要求
    公司中springcloud项目遇到的问题
    实习生在公司的成长
  • 原文地址:https://www.cnblogs.com/VIrtu0s0/p/10519196.html
Copyright © 2011-2022 走看看