1. 程式人生 > >【WOJ 4240】約數個數

【WOJ 4240】約數個數

【題目】

題目描述:

給出整數 x x ,求它的約數個數

為了加大難度,有 m m 次詢問,將每次詢問的答案求和然後模 1

0 9 + 7 10^9+7

輸入格式:

第一行是一個整數 m m

第二行是整數 q 1 q_1 a a b

b c c

q 1 q_1 表示第一次詢問的數字

i i 次詢問 q i = ( q i 1 a + b ) % c q_i=(q_{i−1}∗a+b)\%c

輸出格式:

所有詢問的答案之和模 1 0 9 + 7 10^9+7

樣例資料:

輸入
2
2 2 1 9

輸出
4

提示:

100 % 100\% 的資料保證 m 3 × 1 0 6 , a , b , c 1 0 7 , q i 1 , c 1 m\leq 3\times 10^6, a,b,c\leq10^7,q_i≥1,c≥1


【分析】

看到這道題的資料範圍, q q 1 0 7 10^7 級別的,所以應該是 O ( n ) O(n) 的複雜度

然後打了個暴力,去看正解,發現是用線性篩來篩約數個數,然後就學了一下

首先,新定義兩個陣列 g[i]num[i]g[i] 表示 i i 的約數個數,num[i] 表示 i i 最小質因數的個數

舉個例子,對於 12g[12]=6num[12]=2(因為 12=2*2*3)

有一個結論,如果把一個數分解成 p 1 a 1 p 2 a 2 p k a k p_1^{a_1}\cdot p_2^{a_{2}}\cdots p_{k}^{a_k} ,那麼一個數的約數個數應為 i = 1     k ( a i + 1 ) \prod ^{\;\,k}_{i=1}(a_i+1)

現在分三種情況討論一下:

1、若 i 為質數,那麼顯然 g[i]=2,num[i]=1

2、若 i 為合數

由於線上性篩中,一個數只會被它的最小質因數和除它以外的最大因數篩掉,那麼就設這個最小質因數為 p p ,這個因數為 x x

1)若 x x p p 的倍數,此時 p p 就是 i i x x 的最小質因數,也即若把 x x 表示成 (num[x]+1)*k k k 為其他的質因個數 +1 的乘積), i i 就可以表示成 (num[x]+2)*k

\therefore 此時, g[i]=g[x]/(num[x]+1)*(num[x]+2),num[i]=num[x]+1

2)若 x x 不為 p p 的倍數,因為 p p i i 的最小質因數,所以易知 num[i]=1,並且 i i 相當於只是在 x x 的基礎上多乘了一個 p p ,所以 g[i]=g[x]*2


【程式碼】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10000005
#define Mod 1000000007
using namespace std;
int prime[N],g[N],num[N];
bool mark[N];
void linear_sieves()
{
	int i,j,sum=0;
	memset(mark,true,sizeof(mark));
	mark[0]=mark[1]=false;g[1]=1;
	for(i=2;i<N;++i)
	{
		if(mark[i])
		{
			g[i]=2;
			num[i]=1;
			prime[++sum]=i;
		}
		for(j=1;j<=sum&&i*prime[j]<N;++j)
		{
			mark[i*prime[j]]=false;
			if(i%prime[j]==0)
			{
				num[i*prime[j]]=num[i]+1;
				g[i*prime[j]]=g[i]/(num[i]+1)*(num[i]+2);
				break;
			}
			num[i*prime[j]]=1;
			g[i*prime[j]]=g[i]*2;
		}
	}
}
int main()
{
	linear_sieves();
	int m,i,a,b,c,now;
	scanf("%d%d%d%d%d",&m,&now,&a,&b,&c);
	int ans=g[now];
	for(i=2;i<=m;++i)
	{
		now=(1ll*now*a+b)%c;
		ans=(ans+g[now])%Mod;
	}
	printf("%d",ans);
	return 0;
}