校内互测 分解数 (线性筛+乱搞)

分解数

(dec.cpp/c/pas)

【问题描述】

Dpstr学习了动态规划的技巧以后,对数的分解问题十分感兴趣。

Dpstr用此过程将一个正整数x分解成若干个数的乘积:一开始令集合A中只有一个元素x,每次分解时从A中取一个元素a并找出两个大于1且互质的整数pq,要求pq=a,然后将a分解成两个元素pq,也就是从A中删去a并加入pq。Dpstr把正整数x用该过程能分解的次数的最大值称为x的分解数。

例如66的分解数为2,因为最多分解2次。一种分解过程为:一开始A={66},第1次将66分解为11×6,此时A={11,6},第2次将6分解为2×3,此时A={11,2,3},之后无法分解。还可以知道,11,2,3的分解数均为0,因为它们一开始就无法分解。

不过只分解一个数对Dpstr来说不够有趣。Dpstr生成了一个包含n个正整数的数列a1, a2,..., an,请你回答有多少对正整数(l,r)满足1≤lrn且lcm(al, al+1, ..., ar-1,ar)的分解数恰为k。其中lcm(al, al+1,..., ar-1, ar)表示数列从第l项到第r项的所有数的最小公倍数,特别地,当l=r时,lcm(al)=al由于答案可能很大,只需输出满足条件的正整数对个数除以10,007的余数

 

【输入格式】

输入文件名为dec.in。

第一行包含两个正整数nk,意义如题目所示。

第二行包含n个用空格隔开的正整数,分别表示a1, a2,..., an

 

【输出格式】

输出文件名为dec.out。

输出一行一个整数,表示满足条件的正整数对个数除以10,007的余数。

 

【输入输出样例1】

dec.in

dec.out

3 1

2 3 6

4

见选手目录下的dec/dec1.in和dec/dec1.ans。

 

【输入输出样例1说明】

考虑所有满足1≤lr≤4的正整数对(l,r):

1、对于(1,1),lcm(2)=2,分解数为0;

2、对于(2,2),lcm(3)=3,分解数为0;

3、对于(3,3),lcm(6)=6,分解数为1;

4、对于(1,2),lcm(2,3)=6,分解数为1;

5、对于(2,3),lcm(3,6)=6,分解数为1;

6、对于(1,3),lcm(2,3,6)=6,分解数为1。

其中,6的分解数为1是因为{6}可以分解为{2,3},之后无法分解。

因此共有3对正整数(l,r)满足条件。


【输入输出样例2】

dec.in

dec.out

10 2

2 3 2 6 15 5 5 5 2 3

29

见选手目录下的dec/dec2.in和dec/dec2.ans。

 

【输入输出样例3】

见选手目录下的dec/dec3.in和dec/dec3.ans。

 

【数据规模与约定】

对于20%的数据,1≤n≤10,1≤k≤5,1≤ai≤20;

对于40%的数据,1≤n≤100,1≤k≤10,1≤ai≤100;

对于60%的数据,1≤n≤1,000,1≤k≤1,000,1≤ai≤100,000;

对于100%的数据,1≤n≤1,000,000,1≤k≤5,000,000,1≤ai≤10,000,000。


题解:线性筛+乱搞

用线性筛筛出1e7内的所以质数,同时记录每个数最小的质因子。然后就可以O(loga[i])的将每个数进行质因数分解,每次都除掉当前数的最小值因子。

然后枚举区间的起点,可以随着终点的后移发现lcm是单调不降的。所以可以设两个指针,pos1表示f[l,r]>k的最靠前的位置,pos2表示f[l,r]>=k的区间最靠前的位置,然后每次答案+=(pos1-pos2+1),因为单调性所以每次指针只能向后移,不会向前移,有效的保证了科学的时间复杂度。

#include
#include
#include
#include
#include
#define N 1000003
#define p 10007
using namespace std;
int n,k,pd[N*10],prime[700003],minp[N*10];
int a[N],vis[700003],size,num[N][10],vis1[700003],ans;
void calc()
{
	minp[1]=1;
	for (int i=2;i<=10000000;i++) {
		if (!pd[i]) {
			prime[++prime[0]]=i;
			minp[i]=prime[0];
		}
		for (int j=1;j<=prime[0];j++){
			if (i*prime[j]>10000000) break;
			minp[i*prime[j]]=j;
			pd[i*prime[j]]=1;
			if (i%prime[j]==0) break;
		}
	}
}
inline void add(int x,int v)
{
	for (int i=1;i<=num[x][0];i++) {
		int t=num[x][i];
		if (vis[t]==0&&v==1) vis[0]++;
		vis[t]+=v;
		if (vis[t]==0&&v==-1) vis[0]--;
	}
}
inline void add1(int x,int v)
{
	for (int i=1;i<=num[x][0];i++) {
		int t=num[x][i];
		if (vis1[t]==0&&v==1) vis1[0]++;
		vis1[t]+=v;
		if (vis1[t]==0&&v==-1) vis1[0]--;
	}
}
int main()
{
	freopen("dec.in","r",stdin);
	freopen("dec.out","w",stdout);
	calc(); 
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		int x=a[i];
		while (x!=1) {
			num[i][++num[i][0]]=minp[x];
			int t=prime[minp[x]];
			while (x%t==0) x/=t;
		}
	}
	int l=0,r=0; 
	for (int i=1;i<=n;i++) {
		while (vis[0]k+1) add1(--r,-1);
			ans=(ans+(r-l+1))%p;
		}
		add(i,-1); add1(i,-1);
	}
	printf("%d\n",ans);
}




你可能感兴趣的:(数论)