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
  • 相关阅读:
    夺命雷公狗----仿淘宝的菜单功能
    夺命雷公狗---在js里阻止a标签的跳转和form表单的跳转
    夺命雷公狗mongodb之----mongodb---1---的下载,安装,连接
    夺命雷公狗ThinkPHP项目之----企业网站30之网站前台头部导航的高亮显示
    夺命雷公狗ThinkPHP项目之----企业网站29之网站前台左侧导航的高亮显示
    夺命雷公狗ThinkPHP项目之----企业网站28之网站前台左侧导航的实现
    夺命雷公狗ThinkPHP项目之----企业网站27之网站前台单页的完成(从百度编辑器里面取出文章数据)
    夺命雷公狗ThinkPHP项目之----企业网站26之网站前台列表页的显示和完成分页功能
    夺命雷公狗ThinkPHP项目之----企业网站25之网站前台面包屑导航URL的完善
    夺命雷公狗ThinkPHP项目之----企业网站24之网站前台获取当前栏目和顶级栏目
  • 原文地址:https://www.cnblogs.com/1000Suns/p/10362224.html
Copyright © 2011-2022 走看看