您的位置:首页 > 其它

JZOJ4860【NOIP2016提高A组集训第7场11.4】分解数

2016-11-04 20:37 393 查看

Description

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的余数。

Data Constraint

对于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。

Solution

这道题实质就是求有多少个区间的lcm的分解质因数后的个数恰为m+1。由于对于一个右端点,它的左端点的答案是有二分性的,所以我们考虑用指针。对于一个右端点k,我们设左端点的两个指针分别为i,j,表示左端点在[i,j]内的答案为m+1。同时我们打两个桶,记录[i,k]和[j,k]的lcm有多少质因子,动态维护一下使[i,j]始终合法即可。

现在我们再来考虑一下怎样预处理。我们先用欧拉筛处理处10,000,000内的质数,同时处理处每个数的最小质因子。对于每个输入的数,我们不断除以这个数的最小质因数。所以复杂度为O(N*logN)。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn1=10000000,maxn2=1000000;
int g[maxn2],g1[maxn2];
int b[maxn2][9],d[maxn2],xiao[maxn1];
int n,i,t,j,k,l,m,sum,x,ans,sum1;
bool bz[maxn1+5],bz1;
int main(){
freopen("dec.in","r",stdin);freopen("dec.out","w",stdout);
//  freopen("data.in","r",stdin);
bz[1]=true;
for (i=2;i<=maxn1;i++){
if (!bz[i]) d[++d[0]]=i,xiao[i]=d[0];
for (j=1;j<=d[0];j++){
if (i*d[j]>maxn1) break;
bz[i*d[j]]=true;
xiao[i*d[j]]=min(j,xiao[i]);
if (!(i%d[j])) break;
}
}
scanf("%d%d",&n,&m);
m++;
for (i=1;i<=n;i++){
scanf("%d",&t);
while (t>1){
k=xiao[t];
b[i][++b[i][0]]=xiao[t];
while (!(t%d[k]))
t/=d[k];
}
}
i=1;j=1;
for (k=1;k<=n;k++){
t=0;
for (l=1;l<=b[k][0];l++){
if (!g[b[k][l]]) sum++;
g[b[k][l]]++;
if (!g1[b[k][l]]) sum1++;
g1[b[k][l]]++;
}
if (sum>=m){
while (sum>m){
for (l=1;l<=b[i][0];l++){
g[b[i][l]]--;
if (!g[b[i][l]]) sum--;
}
i++;
}
if (sum==m){
bz1=true;
while (1){
for (l=1;l<=b[j][0];l++){
g1[b[j][l]]--;
if (!g1[b[j][l]]) sum1--;
}
if (sum1<m){
for (l=1;l<=b[j][0];l++){
if (!g1[b[j][l]]) sum1++;
g1[b[j][l]]++;
}
break;
}
j++;
}
if (j>=i) ans+=j-i+1;
}
ans=ans%10007;
}else sum+=t;
}
printf("%d\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: