题目描述
时间限制:1s 内存限制 128MB
(dec.cpp/c/pas)
【问题描述】
Dpstr学习了动态规划的技巧以后,对数的分解问题十分感兴趣。
Dpstr用此过程将一个正整数x分解成若干个数的乘积:一开始令集合A中只有一个元素x,每次分解时从A中取一个元素a并找出两个大于1且互质的整数p,q,要求pq=a,然后将a分解成两个元素p和q,也就是从A中删去a并加入p和q。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≤l≤r≤n且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。
第一行包含两个正整数n,k,意义如题目所示。
第二行包含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≤l≤r≤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,0≤k≤5,1≤ai≤20;
对于40%的数据,1≤n≤100,0≤k≤10,1≤ai≤100;
对于60%的数据,1≤n≤1,000,0≤k≤1,000,1≤ai≤100,000;
对于100%的数据,1≤n≤1,000,000,0≤k≤5,000,000,1≤ai≤10,000,000。
题解
非常好的一道题,个人认为是本场测试最有价值的一道题。
分析题目可以发现,题目实际上是要求有多少个区间,区间内所有的数共有k个不同的质因数。
设
f(l,r)
表示
lcm(al,al+1...ar)
的分解数,显然
f(l,r)<=f(l,r+1)
。
可以考虑先求
f(l,r)>=k
的区间数量 ,然后再求
f(l,r)>k
的区间数量,然后两个答案相减。
之所以这样做是为了保证
l
随着
我们可以按顺序加入
r
,然后每一次移动
线性筛是
O(n)
的,暴力分解质因数是
O(loga)
的,每一个点最多被加或减4次,所以总的时间复杂度是
O(n+4nloga)≈0.94s
,实际上时间和空间都是很卡的,所以也考察压常数和压内存的能力。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define P 10000000
#define N 1000000
#define Mod 10007
int n,k,Max,lp,lq,cntp,cntq,ans1,ans2,ans;
int a[N+1],prime[670000],pr[P+1],p[P+1],q[P+1];
inline int read()
{
int x=0;char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void get_p()
{
for (int i=2;i<=Max;++i)
{
if (!p[i]) prime[++prime[0]]=i,pr[i]=i;
for (int j=1;j<=prime[0]&&i*prime[j]<=Max;++j)
p[i*prime[j]]=1,pr[i*prime[j]]=prime[j];
}
}
inline void add(int x)
{
while (x>1&&!(x%pr[x]))
{
if (!p[pr[x]]) ++cntp;
if (!q[pr[x]]) ++cntq;
++p[pr[x]];
++q[pr[x]];
x/=pr[x];
}
}
inline void addp(int x)
{
while (x>1&&!(x%pr[x]))
{
if (!p[pr[x]]) ++cntp;
++p[pr[x]];
x/=pr[x];
}
}
inline void minup(int x)
{
while (x>1&&!(x%pr[x]))
{
--p[pr[x]];
if (!p[pr[x]]) --cntp;
x/=pr[x];
}
}
inline void addq(int x)
{
while (x>1&&!(x%pr[x]))
{
if (!q[pr[x]]) ++cntq;
++q[pr[x]];
x/=pr[x];
}
}
inline void minuq(int x)
{
while (x>1&&!(x%pr[x]))
{
--q[pr[x]];
if (!q[pr[x]]) --cntq;
x/=pr[x];
}
}
int main()
{
freopen("dec.in","r",stdin);
freopen("dec.out","w",stdout);
n=read();k=read();
for (int i=1;i<=n;++i)
{
a[i]=read();
if (Max<a[i]) Max=a[i];
}
get_p();
lp=1;lq=1;memset(p,0,sizeof(p));cntp=0;cntq=0;
for (int r=1;r<=n;++r)
{
add(a[r]);
while (1)
{
if (lp>r) break;
if ((!k)?(cntp>=k||cntp-1>=k):(cntp-1>=k))
{
minup(a[lp]);
++lp;
}
else break;
}
ans1=(ans1+lp-1)%Mod;
while (1)
{
if (lq>r) break;
if (cntq-1>k)
{
minuq(a[lq]);
++lq;
}
else break;
}
ans2=(ans2+lq-1)%Mod;
}
ans=(((ans1-ans2)%Mod)+Mod)%Mod;
printf("%d\n",ans);
}
总结
①求=的时候可以转化成>=和>来做,这种思路以后要留心。
②有单调性的问题要想到用指针。