zoukankan      html  css  js  c++  java
  • CF1114F Please, another Queries on Array?(线段树,数论,欧拉函数,状态压缩)

    这题我在考场上也是想出了正解的……但是没调出来。

    题目链接:CF原网

    题目大意:给一个长度为 $n$ 的序列 $a$,$q$ 个操作:区间乘 $x$,求区间乘积的欧拉函数模 $10^9+7$ 的值。

    $1le nle 4 imes 10^5,1le qle 2 imes 10^5,1le a_i,xle 300$。时限 5.5s,空限 256MB。


    明显线段树。

    有一个想法是维护区间积的欧拉函数,但是这样时间复杂度和代码复杂度都很高……

    我的做法是维护区间积。而欧拉函数,就是看看区间中包含什么质因子,然后除一下乘一下好了。

    区间积就不用说了。

    包含什么质因子?难道要开bool数组吗?时间复杂度很高……

    经过后台黑科技操作发现 $300$ 以内的质数只有 $62$ 个。明摆着状压的节奏!

    好的,这题做完了。细节的东西在代码中都有。

    对于我的代码实现来说:(以下令 $k=62$)

    建树 $O(kn)$。

    合并节点 $O(1)$。

    下推标记 $O(log n)$。

    区间乘 $O(log^2 n+k)$。

    查询欧拉函数 $O(log n+k)$。

    总时间复杂度应该是 $O((n+q)k+qlog^2n)$。其实跑得不慢,我跑得最慢的点是1934ms。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=400040,mod=1000000007;
    #define lson o<<1,l,mid
    #define rson o<<1|1,mid+1,r
    #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
    #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
    #define MEM(x,v) memset(x,v,sizeof(x))
    inline int read(){
        char ch=getchar();int x=0,f=0;
        while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return f?-x:x;
    }
    int n,q,pri[66],pl,a[maxn],inv[333],f[66],tag1[maxn*4];    //tag1表示区间要乘多少
    ll tag2[maxn*4];    //tag2表示区间会多出哪些质因子(也是压缩过的)
    //为什么要两个标记呢?不能直接对tag1分解质因子吗?
    //因为tag1乘几遍就会被取模,这样看起来质因子就变了。所以额外加一个tag2表示真的质因子集合。
    bool vis[333];
    void init(){
        FOR(i,2,300){
            if(!vis[i]) pri[++pl]=i;
            for(int j=1;j<=pl && i*pri[j]<=300;j++){
                vis[i*pri[j]]=true;
                if(i%pri[j]==0) break;
            }
        }
        inv[1]=1;
        FOR(i,2,300) inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
        FOR(i,1,pl) f[i]=1ll*inv[pri[i]]*(pri[i]-1)%mod;
        //f[i]表示除以p[i],再乘上p[i]-1,便于计算欧拉函数
    }
    inline int qpow(int a,int b){
        int ans=1;
        for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod;
        return ans;
    }
    struct node{
        int pro;ll has;
    }nd[maxn*4];    //一个线段树节点,pro是区间积,has是区间包含哪些质因子(压缩过的)
    void pushup(node &o,node l,node r){    //合并
        o.has=l.has|r.has;    //直接取或
        o.pro=1ll*l.pro*r.pro%mod;
    }
    void setmult(int o,int l,int r,int x,ll y){    //对第o个节点(管辖[l,r])区间乘x,质因子多了y
        tag1[o]=1ll*tag1[o]*x%mod;
        tag2[o]|=y;
        nd[o].pro=1ll*nd[o].pro*qpow(x,r-l+1)%mod;    //记得乘r-l+1次方
        nd[o].has|=y;
    }
    void pushdown(int o,int l,int r){    //下传标记
        if(!tag2[o]) return;
        int mid=(l+r)>>1;
        setmult(lson,tag1[o],tag2[o]);
        setmult(rson,tag1[o],tag2[o]);
        tag1[o]=1;tag2[o]=0;    //记得tag1闲置时是1
    }
    void build(int o,int l,int r){
        tag1[o]=1;tag2[o]=0;
        if(l==r){
            nd[o].pro=a[l];
            FOR(i,1,pl)    //记录质因子集合
                if(a[l]%pri[i]==0) nd[o].has|=1ll<<(i-1);
            return;
        }
        int mid=(l+r)>>1;
        build(lson);build(rson);
        pushup(nd[o],nd[o<<1],nd[o<<1|1]);
    }
    void mult(int o,int l,int r,int ql,int qr,int x,ll y){    //外面调用时先把质因子集合弄好,会省时间
        if(l>=ql && r<=qr){
            setmult(o,l,r,x,y);    //直接设上
            return;
        }
        pushdown(o,l,r);
        int mid=(l+r)>>1;
        if(mid>=ql) mult(lson,ql,qr,x,y);
        if(mid<qr) mult(rson,ql,qr,x,y);
        pushup(nd[o],nd[o<<1],nd[o<<1|1]);
    }
    node query(int o,int l,int r,int ql,int qr){
        if(l>=ql && r<=qr) return nd[o];
        pushdown(o,l,r);
        int mid=(l+r)>>1;
        if(mid<ql) return query(rson,ql,qr);
        if(mid>=qr) return query(lson,ql,qr);
        node ans;
        pushup(ans,query(lson,ql,qr),query(rson,ql,qr));    //合并两边
        return ans;
    }
    int main(){
        init();
        n=read();q=read();
        FOR(i,1,n) a[i]=read();
        build(1,1,n);
        FOR(i,1,q){
            char op[11];
            scanf("%s",op);
            int l=read(),r=read();
            if(op[0]=='M'){    //乘操作
                int x=read();ll y=0;
                FOR(i,1,pl) if(x%pri[i]==0) y|=1ll<<(i-1);    //先处理质因子集合
                mult(1,1,n,l,r,x,y);
            }
            else{    //求欧拉函数操作
                node ans=query(1,1,n,l,r);
                int s=ans.pro;    //区间积
                FOR(i,1,pl) if(ans.has&(1ll<<(i-1))) s=1ll*s*f[i]%mod;    //区间含有第i个质数,那就要除以p[i],再乘上p[i]-1
                printf("%d
    ",s);
            }
        }
    }
    View Code
  • 相关阅读:
    桟错误分析方法
    gstreamer调试命令
    sqlite的事务和锁,很透彻的讲解 【转】
    严重: Exception starting filter struts2 java.lang.NullPointerException (转载)
    eclipse 快捷键
    POJ 1099 Square Ice
    HDU 1013 Digital Roots
    HDU 1087 Super Jumping! Jumping! Jumping!(动态规划)
    HDU 1159 Common Subsequence
    HDU 1069 Monkey and Banana(动态规划)
  • 原文地址:https://www.cnblogs.com/1000Suns/p/10362224.html
Copyright © 2011-2022 走看看