技巧:用唯一分解定理优化因数分解

\(\texttt {Background 1}\)

P3987 我永远喜欢珂朵莉

尽管它的提交记录里满屏的循环展开纯抱粒
但是它的正解是平衡树+树状数组/线段树.
之所以用这个技巧优化不只是因为它有双倍经验,还有就是昨晚上 \(\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;
}
//好像跑得挺快
posted @ 2022-02-11 19:57  Neutral1sed  阅读(52)  评论(0)    收藏  举报