【NOIP 模拟题】[T2]分解数(线性筛+贪心)
【题解】【线性筛+贪心】
【通过分析题意我们能够得出:分解数为当前最小公倍数的质因数个数-1。】
【先用线性筛筛出10^7内的质数,同时记录10^7内的每个数的最小质因子,然后把每个数分解质因数,并存下来。】
【枚举有>=k个不同质因数的区间,枚举区间的右端点,维护左端点的区间[L,R],保证在[L,R]区间中,不同的质因数个数>=k,当区间的右端点不断右移时,区间的左端点区间也只会不断右移,即左端点区间的位置是单调的。这样就可以线性处理】
【用两个桶维护左端点在L和R时不同质因数的个数,当左端点区间满足条件,将R-L+1加入答案】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 10007
#define N 1e7
using namespace std;
int prime[670000],minn[10000005];
int cnt[1000005][10],nm1[1000005],nm2[1000005];
int n,k,tt1,tt2,left,right,ans;
inline void shai()
{
for(int i=2;i<=N;++i)
{
if(!minn[i]) prime[++prime[0]]=i,minn[i]=prime[0];
for(int j=1;j<=prime[0];++j)
{
if(i*prime[j]>N) break;
if(minn[i]<j) minn[i*prime[j]]=minn[i];
else minn[i*prime[j]]=j;
if(!(i%prime[j])) break;
}
}
}
int main()
{
freopen("dec.in","r",stdin);
freopen("dec.out","w",stdout);
int i,j;
shai();
scanf("%d%d",&n,&k);
for(i=1;i<=n;++i)
{
int x;
scanf("%d",&x);
while(x!=1)
{
int t=minn[x];
int p=prime[t];
cnt[i][++cnt[i][0]]=t;
while(!(x%p)) x/=p;
}
}
left=right=1;
k++;
for(i=1;i<=n;++i)
{
for(j=1;j<=cnt[i][0];++j)
{
if(!nm1[cnt[i][j]]) tt1++;
nm1[cnt[i][j]]++;
if(!nm2[cnt[i][j]]) tt2++;
nm2[cnt[i][j]]++;
}
if(tt1>=k)
{
while(tt1>k)
{
for(j=1;j<=cnt[left][0];++j)
{
nm1[cnt[left][j]]--;
if(!nm1[cnt[left][j]]) tt1--;
}
left++;
}
if(tt1==k)
{
while(1)
{
for(j=1;j<=cnt[right][0];++j)
{
nm2[cnt[right][j]]--;
if(!nm2[cnt[right][j]]) tt2--;
}
if(tt2<k)
{
for(j=1;j<=cnt[right][0];++j)
{
if(!nm2[cnt[right][j]]) tt2++;
nm2[cnt[right][j]]++;
}
break;
}
right++;
}
if(right>=left) ans=(ans+(right-left+1)%mod)%mod;
}
}
}
printf("%d\n",ans);
return 0;
}
既然无能更改,又何必枉自寻烦忧