dp
给出一个序列,a1~an;给m个坏素数。不在这m个坏素数之内的素数称为好素数。
对于序列中的每个数,对应一个函数值f(s)满足:如果s的最小质因子p是好素数,则,否则。
另外,我们可以进行以下操作:选择一个r(1<=r<=n),计算a[1]到a[r]的最大公约数g = GCD(a[1], a[2], ..., a[r]),令,...。
我们可以无限次的进行以上操作,问,通过这些操作,我们能得到的 sum=最大值是多少。
我们首先预处理出前缀gcd,(我的代码里时dpg[]),通过题意,我们知道,前面数字进行的操作比后面的多或者一样。所以我们从后往前判断是否要进行操作。每次进行操作时,我们对这个gcd分解质因子,判断它们之中有多少好素数,多少个坏素数,如果这个操作有利于提高我们的sum,就进行此操作,否则就不进行。
同时记录到达当前位置时,已经除去的数,我用dive[]记录。
这样,我们统计到一个数时,如果之后一个位置的数进行过操作,则当前位置的数必然也进行过相同的操作,所以,我们可以用gcd[i]/dive[i+1]表示前i个数当前最大的gcd,进行上面同样的判断,并记录dive[i]。
代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define INF 200000000
int dpg[5005],num[5005],dive[5005],prime[5005],n,m;
int gcd(int a,int b)
{
if(a%b==0)
return b;
else
return gcd(b,a%b);
}
int max(int a,int b)
{
return a>b?a:b;
}
int min(int a,int b)
{
return a>b?b:a;
}
int judge(int x) //判断进行除x的操作,值的变化
{
int i,j,sum;
sum=0;
for(i=1;i<=m;i++)
{
if(x%prime[i]==0)
{
while(x%prime[i]==0) //这里是看了别人的代码,感觉这种处理好坏素数的方法更好
{
x/=prime[i];
sum--;
}
}
}
for(i=2;i*i<=x;i++)
{
if(x%i==0)
{
while(x%i==0)
{
x/=i;
sum++;
}
}
}
if(x>1)
{
sum++;
}
return sum;
}
int main()
{
int i,j,k,l,add,re;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(i=1;i<=n;i++)
{
scanf("%d",&num[i]);
if(i==1)
dpg[i]=num[i];
else
dpg[i]=gcd(max(dpg[i-1],num[i]),min(dpg[i-1],num[i]));
}
for(i=1;i<=m;i++)
scanf("%d",&prime[i]);
dive[n+1]=1;
add=0;
for(i=1,re=0;i<=n;i++)
re+=judge(num[i]);
for(i=n;i>0;i--)
{
if(judge(dpg[i]/dive[i+1])<0)
{
dive[i]=dpg[i];
add-=(judge(dpg[i]));
}
else
{
dive[i]=dive[i+1];
add-=(judge(dive[i+1]));
}
}
printf("%d\n",re+add);
}
return 0;
}