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

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

题目链接: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);
        }
    }
}

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

时间: 2024-08-29 10:35:23

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

bzoj 4026 dC Loves Number Theory (主席树+数论+欧拉函数)

题目大意:给你一个序列,求出指定区间的(l<=i<=r) mod 1000777 的值 还复习了欧拉函数以及线性筛逆元 考虑欧拉函数的的性质,(l<=i<=r),等价于 (p[j]是区间内所有出现过的质数) 那么考虑找出区间内所有出现过的质数,这思路和HH的项链是不是很像?? 由于此题强制在线,所以把树状数组替换成了主席树而已 原来我以前写的主席树一直都是错的......还好推出了我原来错误代码的反例 在继承上一个树的信息时,注意不要破坏现在的树 1 #include <cs

spoj gss2 : Can you answer these queries II 离线&amp;&amp;线段树

1557. Can you answer these queries II Problem code: GSS2 Being a completist and a simplist, kid Yang Zhe cannot solve but get Wrong Answer from most of the OI problems. And he refuse to write two program of same kind at all. So he always failes in co

SPOJ GSS5 Can you answer these queries V (线段树)

原来有一两个人说我不帅的时候,我不以为意,逗我玩而已,后来几乎所有 人都说我不帅,我才真正意识到事态的严重,这社会骗子真是越来越多了... 好吧我承认,这个笑话不好笑,其实我想说的是,做人一定要坚持自己的原则, 哪怕有一天所有人都和你背道而驰,都不要放弃自己当初的梦想,如果有一天, 我们淹没在人海之中,庸碌一生,那是因为我们不够努力,不够勇敢的去面对生活. 每天积累一点点,嗯,满足简单的快乐. ---------------------------------------------------

HDU5152 线段树 + 数论

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5152 ,线段树区间更新 + 点更新 + 数论知识(数论是重点QAQ),好题值得一做. BestCoder Round #24的C题,一道神题,不得不说,出题人的数论学的很好,很多人都没想到2333333不是素数的问题,当时全场爆零.我今天下午开始研究这道题,后来看了好久的标程才懂,惭愧. 一共有两个操作一个询问:1.询问[l , r]区间里的值的和; 2.将点x的值a[x]赋为2a[x]; 3.将区

loj1370(欧拉函数+线段树)

传送门:Bi-shoe and Phi-shoe 题意:给出多个n(1<=n<=1e6),求满足phi(x)>=n的最小的x之和. 分析:先预处理出1~1e6的欧拉函数,然后建立一颗线段树维护最大值,对于每个n询问大于等于n的最左边下标. #pragma comment(linker,"/STACK:1024000000,1024000000") #include <cstdio> #include <cstring> #include <

BZOJ 4034 树上操作(树的欧拉序列+线段树)

刷个清新的数据结构题爽一爽? 题意: 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 注意到操作3,询问x到根的路径之间点权和,容易发现这就是欧拉序列中的前缀和. 所以按照树的欧拉序列建线段树,然后操作1就变成单点修改,操作2,就变成了区间内某些点+a,某些点-a,也容易用tag标记

清华集训 2014--奇数国(线段树&amp;欧拉函数&amp;乘法逆元&amp;状态压缩)

昨天想了一晚...早上AC了... 这么长时间没打线段树,这回居然一次过了... 感觉数论方面应该已经没有太大问题了... 之后要开始搞动态规划之类的东西了... 题意 在一片美丽的大陆上有100000个国家,记为1到100000.这里经济发达,有数不尽的账房,并且每个国家有一个银行.某大公司的领袖在这100000个银行开户时都存了3大洋,他惜财如命,因此会不时地派小弟GFS清点一些银行的存款或者让GFS改变某个银行的存款.该村子在财产上的求和运算等同于我们的乘法运算,也就是说领袖开户时的存款总

hdu 5152 A Strange Problem线段树+欧拉函数

*****************************************BC题解**********************************************************************1003 A Strange Problem 对于操作二,利用公式 当x >= Phi(C), A^x = A ^ (x%Phi(C) + Phi(C)) (mod C) 对于2333333这个模数来说,求18次欧拉函数后就变成了1,所以只需要保存19层第三次操作的加数

LightOJ 1370 Bi-shoe and Phi-shoe 欧拉函数+线段树

分析:对于每个数,找到欧拉函数值大于它的,且标号最小的,预处理欧拉函数,然后按值建线段树就可以了 #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <cmath> #include <map> using namespace std; typedef long long LL; const int N = 1