码迷,mamicode.com
首页 > 其他好文 > 详细

CF1114F Please, another Queries on Array?(线段树,数论,欧拉函数,状态压缩)

时间:2019-02-11 16:53:25      阅读:495      评论:0      收藏:0      [点我收藏+]

标签:push   ref   ase   getch   etc   pushd   区间   eve   bool   

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

题目链接:CF原网

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

$1\le n\le 4\times 10^5,1\le q\le 2\times 10^5,1\le a_i,x\le 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+q\log^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\n",s);
        }
    }
}
View Code

 

CF1114F Please, another Queries on Array?(线段树,数论,欧拉函数,状态压缩)

标签:push   ref   ase   getch   etc   pushd   区间   eve   bool   

原文地址:https://www.cnblogs.com/1000Suns/p/10362224.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!