1. 程式人生 > 實用技巧 >【洛谷P5071】此時此刻的光輝

【洛谷P5071】此時此刻的光輝

題目

題目連結:https://www.luogu.com.cn/problem/P5071
珂朵莉給你了一個長為 \(n\) 的序列,有 \(m\) 次查詢,每次查詢一段區間的乘積的約數個數 \(\bmod 19260817\) 的值。
\(n,m\leq 10^5,a_i\leq 10^9\)

思路

由於 \(2\times 3\times 5\times 7\times 11\times 13\times 17\times 19\times 23\times =6469693230>10^9\),所以 \(10^9\) 以內每個數最多有 \(10\) 個不同的質因子。
我們只需要知道區間 \([l,r]\)

所有質因子的次數乘積即可。由於整個數列質因子最多有 \(10n\) 個,所以可以開桶記錄,然後上莫隊。
雖然複雜度是 \(O(m\sqrt{n})\),但是毒瘤 lxl 肯定不會放這種演算法過去的。需要一些卡常。


首先我們發現,\(1000\) 以內的質數只有 \(168\) 個,而 \(10^9\) 以內的數除去小於 \(1000\) 的質因子外,最多還有兩個質因子。
所以我們可以設 \(\mathrm{sum}_{i,j}\) 表示序列中前 \(i\) 個數,小於 \(1000\)\(168\) 個質數中,第 \(j\) 個質數的數量。
那麼詢問的時候只需要把大於 \(1000\) 的質因子的貢獻計算出來,然後列舉這 \(168\)

個質數補上貢獻即可。這樣我們對於每一個數最多隻需要再維護兩個質因子即可。
然後不要採用根號的做法求質因子,可以上 Pollard-Rho。同時計算出一個大於 \(1000\) 的質因子後,直接作除法就可以計算出另一個質因子。
那麼我們需要給每一個超過 \(1000\) 的質因子離散化。直接 unordered_map 即可。
然後莫隊維護超過 \(1000\) 的因子即可。寫的正常一些就可以過吧。
時間複雜度 \(O(m\sqrt{n}+168n)\)

程式碼

C++11 3.55 KB 2.03s 74.64MB

#include <bits/stdc++.h>
#define reg register
using namespace std;
typedef long long ll;
typedef long double ld;

const int prm[169]={0,2,7,61,3,5,11,13,17,19,23,29,31,37,41,43,47,53,59,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997};
const int N=100010,M=170,MOD=19260817;
int n,m,T,tot,maxd,a[N],b[N],c[N],bel[N],inv[N*2],cnt[N*2],sum[M][N];
ll res,ans[N];
map<int,int> id;

int read()
{
	int d=0; char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

void write(int x)
{
	if (x>9) write(x/10);
	putchar(x%10+48);
}

struct Query
{
	int l,r,id;
	
	friend bool operator <(Query x,Query y)
	{
		return (bel[x.l]==bel[y.l])?((bel[x.l]&1)?(x.r<y.r):(x.r>y.r)):(bel[x.l]<bel[y.l]);
	}
}ask[N];

ll fpow(ll x,ll k,ll mod=MOD)
{
	ll ans=1;
	for (;k;k>>=1,x=x*x%mod)
		if (k&1) ans=ans*x%mod;
	return ans;
}

int gcd(int x,int y)
{
	return y?gcd(y,x%y):x;
}

bool MR(int n)
{
	for (int i=1;i<=3;i++)
	{
		if (n==prm[i]) return 1;
		int m=n-1,power=fpow(prm[i],m,n);
		while (power==1 && !(m&1))
			m>>=1,power=fpow(prm[i],m,n);
		if (power!=1 && power!=n-1) return 0;
	}
	return 1;
}

int work(int n)
{
	int s=0,t=0,c=rand()%(n-1),lim=1,val=1;
	for (int i=1;;i++)
	{
		t=(1LL*t*t+c)%n; val=1LL*val*abs(s-t)%n;
		if (!(i%127) || i==lim)
		{
			int d=gcd(val,n);
			if (d>1) return d;
			if (i==lim) s=t,val=1,lim<<=1;
		}
	}
}

void PR(int n)
{
	if (n<maxd || n<2) return;
	if (MR(n)) { maxd=n; return; }
	int p=work(n);
	while (p>=n) p=work(n);
	while (!(n%p)) n/=p;
	PR(n); PR(p);
}

inline void add(int x)
{
	if (b[x]) res=res*inv[cnt[b[x]]]%MOD*(cnt[b[x]]+=1)%MOD;
	if (c[x]) res=res*inv[cnt[c[x]]]%MOD*(cnt[c[x]]+=1)%MOD;
}

inline void del(int x)
{
	if (b[x]) res=res*inv[cnt[b[x]]]%MOD*(cnt[b[x]]-=1)%MOD;
	if (c[x]) res=res*inv[cnt[c[x]]]%MOD*(cnt[c[x]]-=1)%MOD;
}

signed main()
{
	srand(1023);
	n=read(); m=read();
	T=sqrt(n)+1;
	for (int i=1;i<=n;i++)
	{
		a[i]=read();
		bel[i]=(i-1)/T+1;
		for (int j=1;j<=168;j++)
		{
			sum[j][i]=sum[j][i-1];
			for (;!(a[i]%prm[j]) && a[i]>=prm[j];a[i]/=prm[j])
				sum[j][i]++;
		}
		PR(a[i]);
		b[i]=maxd; maxd=0;
		if (b[i] && b[i]!=a[i]) c[i]=a[i]/b[i];
		if (b[i])
		{
			if (id.find(b[i])==id.end()) id[b[i]]=++tot;
			b[i]=id[b[i]];
		}
		if (c[i])
		{
			if (id.find(c[i])==id.end()) id[c[i]]=++tot;
			c[i]=id[c[i]];
		}
	}
	cerr<<tot;
	for (int i=1;i<=m;i++)
		ask[i]=(Query){read(),read(),i};
	sort(ask+1,ask+1+m);
	inv[0]=inv[1]=cnt[1]=1;
	for (int i=2;i<=2*n;i++)
	{
		inv[i]=(-1LL*MOD/i*inv[MOD%i]+10LL*MOD*MOD)%MOD;
		cnt[i]=1;
	}
	res=1; id[0]=id[1]=0;
	for (reg int i=1,l=1,r=0;i<=m;i++)
	{
		int k=ask[i].id;
		for (;l>ask[i].l;l--) add(l-1);
		for (;r<ask[i].r;r++) add(r+1);
		for (;l<ask[i].l;l++) del(l);
		for (;r>ask[i].r;r--) del(r);
		ans[k]=res;
		for (reg int j=1;j<=168;j++)
			ans[k]=ans[k]*(sum[j][r]-sum[j][l-1]+1)%MOD;
	}
	for (int i=1;i<=m;i++)
		write(ans[i]),putchar(10);
	return 0;
}