传送门
解析:
首先这神奇的函数可以线性筛出来,只要弄明白线性筛的一个性质,当筛到某个数的时候,枚举的质数必然是它的最小质因子就行了。
然后其实也可以在O(nlnn)O(\frac{\sqrt n}{ln\sqrt{n}})O(lnnn)时间内处理出单个数的函数值。
fff的实现多种多样,这里就不细讲,主要讲一下两种做法O(n2)O(n^2)O(n2)的DPDPDP和O(n)O(n)O(n)的贪心递推(不考虑预处理复杂度)
其实O(n2)O(n^2)O(n2)的DPDPDP就是数字三角形的修改版。考虑如果我们除了一个前缀的gcdgcdgcd那么这个前缀的最后一个位置开始的后面位置都不可能再次被更改,而这个前缀的前缀仍然有可能继续被更改,所以我们将前缀每次修改能够达到的值记录下来。而每次修改的gcdgcdgcd就是在上面选择一个前缀。并且后面的数的决策位置不能在前面的数的决策位置之前,那么维护一个后缀最大值就可以转移了。
而O(n)O(n)O(n)的贪心递推也十分的妙啊,处理出所有前缀gcdgcdgcd,发现fff函数还有一个性质:对于j∣ij|ij∣i,f(i)−f(j)=f(i/j)f(i)-f(j)=f(i/j)f(i)−f(j)=f(i/j),这个很显然,因为实际上fff函数的计算就是分解质因数,然后按照次数和好坏统计贡献,证明就免了,十分显然。
所以预处理出所有前缀gcdgcdgcd的fff函数值,然后倒过来贪心,如果除以当前前缀的gcdgcdgcd能够获得正的贡献,那就直接除,不然放弃。维护一下当前已经除了的前缀gcdgcdgcd的fff值就可以统计答案了。
代码(DP):
#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
inline int gcd(int a,int b){
int tmp;
while(b){
tmp=a-a/b*b;
a=b;
b=tmp;
}
return a;
}
cs int P=1000006;
bool mark[P];
int prime[P],pcnt;
bool isbad[P];
int f[P];
set<int> badprimes;
inline void linear_sieves(int len=P-6){
for(int re i=2;i<=len;++i){
if(!mark[i])prime[++pcnt]=i,f[i]=isbad[i]?-1:1;
for(int re j=1;j<=pcnt&&i*prime[j]<=len;++j){
mark[i*prime[j]]=true;
f[i*prime[j]]=f[i]+((isbad[prime[j]])?-1:1);
if(i%prime[j]==0)break;
}
}
}
inline bool isbadprime(int a){
if(a<=P-6)return isbad[a];
else return badprimes.find(a)!=badprimes.end();
}
tr1::unordered_map<int,int> FF;
inline int F(int a){
if(a<=P-6)return f[a];
else if(FF.find(a)!=FF.end())return FF[a];
int pos=0;
for(int re i=1;i<=pcnt&&prime[i]*prime[i]<=a;++i)if(a%prime[i]==0){
pos=i;break;
}
if(pos)return FF[a]=(isbad[prime[pos]]?-1:1)+F(a/prime[pos]);
else return FF[a]=isbadprime(a)?-1:1;
}
cs int N=5003;
int n,m;
int a[N],g[N];
vector<int> ed[N];
int suf;
int ans;
int maxn[N][N];
signed main(){
n=getint();
m=getint();
for(int re i=1;i<=n;++i){
a[i]=getint();
if(i>1)g[i]=gcd(g[i-1],a[i]);
else g[i]=a[i];
ed[i].push_back(a[i]);
}
for(int re i=1;i<=m;++i){
int b=getint();
if(b<=P-6)isbad[b]=true;
badprimes.insert(b);
}
linear_sieves();
for(int re i=1;i<=n;++i)ans+=F(a[i]);
for(int re i=n;i;--i){
int now=g[i];
int sum=suf;
suf+=F(a[i]);
for(int re j=i;j;--j)sum+=F(a[j]/now),ed[j].push_back(a[j]/now);
ans=max(ans,sum);
}
memset(maxn,-0x3f,sizeof maxn);
for(int re i=ed[1].size()-1;~i;--i){
maxn[1][i]=max(F(ed[1][i]),maxn[1][i+1]);
}
for(int re i=2;i<=n;++i){
for(int re j=ed[i].size()-1;~j;--j){
maxn[i][j]=max(maxn[i][j+1],F(ed[i][j])+maxn[i-1][j]);
}
}
for(int re i=ed[n].size()-1;~i;--i){
ans=max(ans,maxn[n][i]);
}
cout<<ans;
return 0;
}