把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷5071】[Ynoi2015] 此时此刻的光辉(莫队)

点此看题面

大致题意: 给出一个序列,每次询问一个区间中所有数乘积的约数个数。

前言

第一次看到这题的时候看到题解区清一色的\(Pollard-Rho\),果断弃疗。

今天重新点开,再重新看了一遍题面,才发现这题似乎根本不用什么高级算法,只是普通的莫队+普通的乱搞而已。

只不过我写得比较菜,一开始还\(T\)掉了一个点。后来在闪指导的指导下,把预处理求逆元时的快速幂求法改成了线性求逆元,结果就\(2.31s\)卡过了。。。

\(STO\ hl666\ ORZ\)

前置知识

对于一个数\(n\),设其质因数分解得到\(p_1^{k_1}\times p_2^{k_2}\times...\times p_m^{k_m}\)\(p_1,p_2,...,p_m\)为互不相同的质数),则其约数个数就是\(\prod_{i=1}^m(k_i+1)\)

这个式子的意思实际上就是枚举每一种质因子选几个。

莫队

由“前置知识”我们得到启发,要求约数个数,实际上只需要维护每个质因子的个数即可。

怎么维护呢?毕竟这可是Ynoi啊,自然是用莫队喽!

由于每个数最多只有\(10\)种质因子(因为最小的\(10\)个质数乘积已经超过\(10^9\)了),因此我们只要记录下这些质因子,就能方便快捷地维护了。

质因数分解

考虑如何求出一个数的质因子。当然是Pollard-Rho呀。

我们可以先线性筛,筛出\(\sqrt{10^9}\)范围内的全部质数。

对于每个需要分解的数,我们从小到大枚举质数,记下它的质因子并将该质因子除去(注意,这里是一个优化)。

如果某一刻枚举到的质因子的平方大于该数了,就停止枚举。

然后考虑剩下的这个数,如果它不为\(1\),则一定是一个质数(显然)。

于是我们就实现了质因数分解。

具体实现可以详见代码。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define X 19260817
using namespace std;
int n,m,a[N+5],k[N+5],p[N+5][15],t[N+5][15],sz,bl[N+5],Inv[30*N+5],v[103406],ans[N+5];
map<int,int> pos;
struct Qry
{
	int p,l,r;I Qry(CI i=0,CI a=0,CI b=0):p(i),l(a),r(b){}
	I bool operator < (Con Qry& o) Con {return bl[l]^bl[o.l]?bl[l]<bl[o.l]:(bl[l]&1?r<o.r:r>o.r);}//莫队排序
}q[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define pc(c) (C==E&&(clear(),0),*C++=c)
		#define tn (x<<3)+(x<<1)
		#define D isdigit(c=tc())
		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
	public:
		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
		Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
		Tp I void writeln(Con Ty& x) {write(x),pc('\n');}
		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
struct LinearSieve
{
	#define LIM 31622
	int Pt,P[LIM+5];
	I LinearSieve()//线性筛筛质数
	{
		for(RI i=2,j,t;i<=LIM;++i) for(!P[i]&&(P[++Pt]=i,pos[i]=Pt),
			j=1;j<=Pt&&(t=i*P[j])<=LIM;++j) if(P[t]=1,!(i%P[j])) break;
	}
}S;
I void Init(CI id,RI v)
{
	static int cnt=S.Pt;
	RI i;for(i=1;i<=S.Pt&&1LL*S.P[i]*S.P[i]<=v;++i) if(!(v%S.P[i]))//枚举
		{p[id][++k[id]]=i;W(v/=S.P[i],++t[id][k[id]],!(v%S.P[i]));}//统计,并注意除去
	if(v==1) return;if(!pos[v]) pos[v]=++cnt;p[id][++k[id]]=pos[v],t[id][k[id]]=1;//如果是1就不管,如果是一个新质数就记下来
}
int main()
{
	RI i,x;for(F.read(n),F.read(m),sz=sqrt(n),i=1;i<=n;++i) F.read(x),Init(i,x),bl[i]=(i-1)/sz+1;//读入,初始化
	for(i=1;i<=m;++i) F.read(q[i].l),F.read(q[i].r),q[i].p=i;sort(q+1,q+m+1);//读入询问,排序
	for(Inv[0]=Inv[1]=1,i=2;i<=30*N;++i) Inv[i]=X-1LL*X/i*Inv[X%i]%X;//线性求逆元
	#define U(x,op) for(j=1;j<=k[x];++j)\
		res=1LL*res*Inv[v[p[x][j]]+1]%X,res=1LL*res*((v[p[x][j]]+=op*t[x][j])+1)%X;//莫队修改
	RI j,L=1,R=0,res=1;for(i=1;i<=m;++i)//枚举询问,莫队
	{
		W(R<q[i].r) {++R;U(R,1);}W(L>q[i].l) {--L;U(L,1);}
		W(R>q[i].r) {U(R,-1);--R;}W(L<q[i].l) {U(L,-1);++L;}ans[q[i].p]=res;
	}
	for(i=1;i<=m;++i) F.writeln(ans[i]);return F.clear(),0;//输出答案
}
posted @ 2020-05-10 20:36  TheLostWeak  阅读(132)  评论(0编辑  收藏  举报