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

题目链接: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=,mod=;
#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=,f=;
while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
return f?-x:x;
}
int n,q,pri[],pl,a[maxn],inv[],f[],tag1[maxn*]; //tag1表示区间要乘多少
ll tag2[maxn*]; //tag2表示区间会多出哪些质因子(也是压缩过的)
//为什么要两个标记呢?不能直接对tag1分解质因子吗?
//因为tag1乘几遍就会被取模,这样看起来质因子就变了。所以额外加一个tag2表示真的质因子集合。
bool vis[];
void init(){
FOR(i,,){
if(!vis[i]) pri[++pl]=i;
for(int j=;j<=pl && i*pri[j]<=;j++){
vis[i*pri[j]]=true;
if(i%pri[j]==) break;
}
}
inv[]=;
FOR(i,,) inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
FOR(i,,pl) f[i]=1ll*inv[pri[i]]*(pri[i]-)%mod;
//f[i]表示除以p[i],再乘上p[i]-1,便于计算欧拉函数
}
inline int qpow(int a,int b){
int ans=;
for(;b;b>>=,a=1ll*a*a%mod) if(b&) ans=1ll*ans*a%mod;
return ans;
}
struct node{
int pro;ll has;
}nd[maxn*]; //一个线段树节点,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+)%mod; //记得乘r-l+1次方
nd[o].has|=y;
}
void pushdown(int o,int l,int r){ //下传标记
if(!tag2[o]) return;
int mid=(l+r)>>;
setmult(lson,tag1[o],tag2[o]);
setmult(rson,tag1[o],tag2[o]);
tag1[o]=;tag2[o]=; //记得tag1闲置时是1
}
void build(int o,int l,int r){
tag1[o]=;tag2[o]=;
if(l==r){
nd[o].pro=a[l];
FOR(i,,pl) //记录质因子集合
if(a[l]%pri[i]==) nd[o].has|=1ll<<(i-);
return;
}
int mid=(l+r)>>;
build(lson);build(rson);
pushup(nd[o],nd[o<<],nd[o<<|]);
}
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)>>;
if(mid>=ql) mult(lson,ql,qr,x,y);
if(mid<qr) mult(rson,ql,qr,x,y);
pushup(nd[o],nd[o<<],nd[o<<|]);
}
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)>>;
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,,n) a[i]=read();
build(,,n);
FOR(i,,q){
char op[];
scanf("%s",op);
int l=read(),r=read();
if(op[]=='M'){ //乘操作
int x=read();ll y=;
FOR(i,,pl) if(x%pri[i]==) y|=1ll<<(i-); //先处理质因子集合
mult(,,n,l,r,x,y);
}
else{ //求欧拉函数操作
node ans=query(,,n,l,r);
int s=ans.pro; //区间积
FOR(i,,pl) if(ans.has&(1ll<<(i-))) s=1ll*s*f[i]%mod; //区间含有第i个质数,那就要除以p[i],再乘上p[i]-1
printf("%d\n",s);
}
}
}

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

  1. Codeforces 1114F Please, another Queries on Array? [线段树,欧拉函数]

    Codeforces 洛谷:咕咕咕 CF少有的大数据结构题. 思路 考虑一些欧拉函数的性质: \[ \varphi(p)=p-1\\ \varphi(p^k)=p^{k-1}\times (p-1)= ...

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

    题目大意:给你一个序列,求出指定区间的(l<=i<=r) mod 1000777 的值 还复习了欧拉函数以及线性筛逆元 考虑欧拉函数的的性质,(l<=i<=r),等价于 (p[ ...

  3. [Codeforces 266E]More Queries to Array...(线段树+二项式定理)

    [Codeforces 266E]More Queries to Array...(线段树+二项式定理) 题面 维护一个长度为\(n\)的序列\(a\),\(m\)个操作 区间赋值为\(x\) 查询\ ...

  4. Codeforces 1114F Please, another Queries on Array? 线段树

    Please, another Queries on Array? 利用欧拉函数的计算方法, 用线段树搞一搞就好啦. #include<bits/stdc++.h> #define LL ...

  5. 暑假集训单切赛第一场 CF 266E More Queries to Array(线段树+二项式展开式)

    比赛时,第二题就是做的这个,当时果断没仔细考虑,直接用线段树暴力求.结果易想而知,超时了. 比赛后搜了搜题解,恍然大悟. 思路:显然用线段树,但是由于每次查询都会有变,所以不可能存储题目中的式子.   ...

  6. [Codeforces266E]More Queries to Array...——线段树

    题目链接: Codeforces266E 题目大意:给出一个序列$a$,要求完成$Q$次操作,操作分为两种:1.$l,r,x$,将$[l,r]$的数都变为$x$.2.$l,r,k$,求$\sum\li ...

  7. CF383C Propagating tree (线段树,欧拉序)

    \(tag\)没开够\(WA\)了一发... 求出\(dfs\)序,然后按深度分类更新与查询. #include <iostream> #include <cstdio> #i ...

  8. Please, another Queries on Array?(Codeforces Round #538 (Div. 2)F+线段树+欧拉函数+bitset)

    题目链接 传送门 题面 思路 设\(x=\prod\limits_{i=l}^{r}a_i\)=\(\prod\limits_{i=1}^{n}p_i^{c_i}\) 由欧拉函数是积性函数得: \[ ...

  9. CF1114F Please, another Queries on Array?

    CF1114F Please, another Queries on Array? 考虑用线段树维护取模后的区间积和真正的区间积所含有的质因子. 每次询问查得这两个值后,一乘一除,即可算出该区间积的欧 ...

随机推荐

  1. 【JVM.1】java内存区域与内存溢出

    鲁迅曾说过:Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的“高墙”,墙外面的人想进来,墙里面的人想出去. 一.虚拟机内存分布 Java虚拟机在执行Java程序的过程中会把它所管理的内存 ...

  2. NFS共享文件系统部署

    1. 概述 本篇博客主要是介绍如何安装和使用NFS服务. 2. 安装软件包 首先确认系统是否已经安装相应的软件包,执行命:rpm -qa | egrep "rpcbind|nfs-utils ...

  3. VS2010、VS2012、VS2013、VS2015、VS2017各版本产品激活秘钥

    Visual Studio 2017(VS2017) 企业版 Enterprise 注册码:NJVYC-BMHX2-G77MM-4XJMR-6Q8QF Visual Studio 2017(VS201 ...

  4. youtube下载工具

    Youtube是一个全球性的视频分享网站,其种类之多,内容之丰富,是大家有目共睹的.特别是原创视频更是多不胜数, 每分钟都有400+小时的youtube视频上传,每天都有30亿+的视频被观看.随着视频 ...

  5. CentOS 6下gcc升级的操作记录(由默认的4.4.7升级到6.4.0版本)

    机房一台centos6.9机器部署了jenkins发布系统,开发人员在用node编译js,发现依赖的gcc版本低了,故需要将gcc升级到高版本(至少5.0版本以上),这里选择升级到6.4.0版本,下面 ...

  6. Centos下堡垒机Jumpserver V3.0环境部署完整记录(1)-安装篇

    由于来源身份不明.越权操作.密码泄露.数据被窃.违规操作等因素都可能会使运营的业务系统面临严重威胁,一旦发生事故,如果不能快速定位事故原因,运维人员往往就会背黑锅.几种常见的运维人员背黑锅场景:1)由 ...

  7. oracle数据恢复方法

    https://www.cnblogs.com/hqbhonker/p/3977200.html

  8. 【CV】ICCV2015_Learning Temporal Embeddings for Complex Video Analysis

    Learning Temporal Embeddings for Complex Video Analysis Note here: it's a review note on novel work ...

  9. Sublime Text3前端必备插件

    安装Package Control 在安装插件之前,需要让sublime安装Package Control.打开Sublime Text的控制台,快捷键ctrl + ~,在控制台中输入以下代码. im ...

  10. shell脚本--内容查找之grep命令

    grep命令可以检索文件中包含关键字(可以使用正则)的行,默认区分大小写. ubuntu@ubuntu:~/test$ cat test.txt this is linux this is Linux ...