技巧:用唯一分解定理优化因数分解
\(\texttt {Background 1}\)
尽管它的提交记录里满屏的循环展开纯抱粒,
但是它的正解是平衡树+树状数组/线段树.
之所以用这个技巧优化不只是因为它有双倍经验,还有就是昨晚上 \(\tt lzx\) 跑过来求一个分解 \(n \le 1e15\) 质因数的程序(
然而我下午试了下,打锅了
\(\texttt {Background 2}\)
其实朴素的球阀也能过(上面的链接):
for(ri j=1;j*j<=a[i];j++){
if(!(a[i]%j)){
p[j].push_back(i);
ri t=a[i]/j;
if(t!=j)
p[t].push_back(i);
}
}
复杂度 \(O(\sqrt{n})\) , \(n\) 是要分解的数
这道题时限比较大,能过
但是它就艹不过在线加强版
其实加强版本身就不是用平衡树实现的,而是并查姬
所以需要优化
然后DPair神仙的求助帖里我瞄到了我写锅的质数分解法
所以就直接上这个了
\(\texttt {Main}\)
唯一分解定理:
对于合数 \(n\) ,它可以表示为唯一的分解形式
\[n=p_{1}^{a_{1}}p_{2}^{a_{2}}p_{3}^{a_{3}}...p_{k}^{a_{k}}
\]
,其中
\[p_i \in Prime
\]
.
所以 \(n\) 的所有因数在把 \(n\) 进行这样分解之后就可以组合求出了.
先预处理 \([1,Max_n]\) 的质数,这个线性筛一遍
然后对于 \(n_i\) ,用 \(\le \sqrt{n_i}\) 的质数分解它
最后剩下的一定是 \(> \sqrt{n_i}\) 的某个质数或者是 \(1\)
因为两个质数 \(A,B>\sqrt{n_i}\) 的积一定大于 \(n_i\)
能分解出的 \(>\sqrt{n_i}\) 的质数最多只有一个.
这样就变成了 \(O(\,P+D+Q\,)\) , \(P\) 是 \(\sqrt{n}\) 以内的质数个数, \(D=\sum_{i=1}^{k} a_i\) 为次数和, \(Q\) 是因数个数.
质数比较稀疏,所以要优一点
滞胀分析
实现:
euler(maxa);
for(ri i=1;i<=n;i++){
cntr=0,fac[++cntr]=1; ri t=a[i];
for(ri j=1;j<=prime[0]&&prime[j]*prime[j]<=a[i];j++){
ri tim(0);
while(!(t%prime[j])) //分解pi^ai
++tim,t/=prime[j];
ri temp(cntr),val(prime[j]);
while(tim--){ //乘法原理
for(ri k=1;k<=cntr;k++)
fac[++temp]=fac[k]*val;
val*=prime[j];
} cntr=temp;
}
if(t!=1){ //剩下一个 >sqrt(n) 的质数
ri temp(cntr);
for(ri j=1;j<=cntr;j++)
fac[++temp]=fac[j]*t;
cntr=temp;
}
for(ri j=2;j<=cntr;j++)
p[fac[j]].push_back(i); //存储含有因数k的下标i
}
然后稍微卡了一点常,加强版就过了.
\(\texttt {Problem Code}\)
//P3987
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define ri register int
#define MAXN 500001
#define ll long long
#define u32 unsigned int
#define g() getchar()
#define pc(a) putchar(a)
#define Tp template<typename T>
using namespace std;
namespace SlowIO{
Tp inline void rd(T &x){
x=0; bool f=1; char in=g();
while(!isdigit(in)) f&=(in!='-'),in=g();
while(isdigit(in)) x=(x<<3)+(x<<1)+(in^48),in=g();
x*=((f<<1)-1);
}
Tp inline void op(T x){
if(x<0) pc('-'),x=-x;
if(x>=10) op(x/10);
pc(x%10+48);
}
Tp inline void writeln(T x){ op(x),pc('\n'); }
Tp inline void writesp(T x){ op(x),pc(' '); }
}; using namespace SlowIO;
int n,m;
int a[MAXN];
namespace Mt19937{
#define TIME chrono::system_clock::now().time_since_epoch().count()
#ifdef debug
u32 Seed=19260817;
#else
u32 Seed=TIME;
#endif
mt19937 rnd(Seed);
}; using namespace Mt19937;
namespace BitInTree{
ll tr[MAXN<<4];
inline void change(int x,int v){
while(x<=n)
tr[x]+=v,x+=lowbit(x);
}
inline ll query(int x){
register ll ret=0;
while(x)
ret+=tr[x],x-=lowbit(x);
return ret;
}
}; using namespace BitInTree;
namespace fhq_treap{
int root[MAXN],tot;
int val[MAXN*80],son[MAXN*80][2];
#define Pii pair<int,int>
#define Rp register Pii
#define f(i) i.first
#define s(i) i.second
#define lef(a) son[a][0]
#define rig(a) son[a][1]
inline int create(int x){
ri ret=++tot;
val[ret]=x; return ret;
}
int s[MAXN],ptr;
inline int build(int l,int r){
if(l>r) return 0;
ri mid=l+r>>1,ret=create(s[mid]);
lef(ret)=build(l,mid-1);
rig(ret)=build(mid+1,r);
return ret;
}
inline Pii split(int now,int k){
if(!now) return {0,0};
Rp ret(0,0);
if(k>=val[now]){
ret=split(rig(now),k);
rig(now)=f(ret),f(ret)=now;
} else{
ret=split(lef(now),k);
lef(now)=s(ret),s(ret)=now;
} return ret;
}
inline int merge(int l,int r){
if(!l||!r) return l|r;
if(rnd()<rnd()){
rig(l)=merge(rig(l),r);
return l;
} else{
lef(r)=merge(l,lef(r));
return r;
}
}
inline void modify(int x,int v){
if(!x) return;
modify(son[x][0],v);
if(!(a[val[x]]%v))
change(val[x],-a[val[x]]),
a[val[x]]/=v,
change(val[x],a[val[x]]);
if(!(a[val[x]]%v))
s[++ptr]=val[x];
modify(son[x][1],v);
}
inline void operate(int l,int r,int x){
Rp t1=split(root[x],l-1);
Rp t2=split(s(t1),r);
ptr=0,modify(f(t2),x);
ri t=build(1,ptr);
root[x]=merge(f(t1),merge(t,s(t2)));
}
}; using namespace fhq_treap;
vector<int> p[MAXN];
static int prime[MAXN>>1];
bitset<MAXN> vis;
inline void euler(int target){
prime[0]=1,prime[1]=2;
for(ri i=3;i<=target;++i){
if(!vis[i])
prime[++prime[0]]=i;
for(ri j=1;j<=prime[0]&&i*prime[j]<=target;++j){
vis[i*prime[j]]=1;
if(!(i%prime[j])) break;
}
}
}
int fac[MAXN],cntr;
int main()
{
ri maxa=0;
rd(n),rd(m);
for(ri i=1;i<=n;i++){
rd(a[i]),change(i,a[i]);
maxa=max(maxa,a[i]);
} euler(maxa);
for(ri i=1;i<=n;i++){
cntr=0,fac[++cntr]=1; ri t=a[i];
for(ri j=1;j<=prime[0]&&prime[j]*prime[j]<=a[i];j++){
ri tim(0);
while(!(t%prime[j]))
++tim,t/=prime[j];
ri temp(cntr),val(prime[j]);
while(tim--){
for(ri k=1;k<=cntr;k++)
fac[++temp]=fac[k]*val;
val*=prime[j];
} cntr=temp;
}
if(t!=1){
ri temp(cntr);
for(ri j=1;j<=cntr;j++)
fac[++temp]=fac[j]*t;
cntr=temp;
}
for(ri j=2;j<=cntr;j++)
p[fac[j]].push_back(i);
}
for(ri i=2;i<=maxa;i++){
ptr=0;
for(auto x:p[i]) s[++ptr]=x;
root[i]=build(1,ptr);
}
ri opt,l,r,x;
while(m--){
rd(opt),rd(l),rd(r);
if(opt&1){
rd(x); if(x==1) continue;
operate(l,r,x);
} else
writeln(query(r)-query(l-1));
}
return 0;
}
//好像跑得挺快