208.10.27【CodeForces402】D. Upgrading Array(数论)(DP)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/83450815

传送门


解析:

首先这神奇的函数可以线性筛出来,只要弄明白线性筛的一个性质,当筛到某个数的时候,枚举的质数必然是它的最小质因子就行了。

然后其实也可以在 O ( n l n n ) O(\frac{\sqrt n}{ln\sqrt{n}}) 时间内处理出单个数的函数值。

f f 的实现多种多样,这里就不细讲,主要讲一下两种做法 O ( n 2 ) O(n^2) D P DP O ( n ) O(n) 的贪心递推(不考虑预处理复杂度)

其实 O ( n 2 ) O(n^2) D P DP 就是数字三角形的修改版。考虑如果我们除了一个前缀的 g c d gcd 那么这个前缀的最后一个位置开始的后面位置都不可能再次被更改,而这个前缀的前缀仍然有可能继续被更改,所以我们将前缀每次修改能够达到的值记录下来。而每次修改的 g c d gcd 就是在上面选择一个前缀。并且后面的数的决策位置不能在前面的数的决策位置之前,那么维护一个后缀最大值就可以转移了。

O ( n ) O(n) 的贪心递推也十分的妙啊,处理出所有前缀 g c d gcd ,发现 f f 函数还有一个性质:对于 j i j|i f ( i ) f ( j ) = f ( i / j ) f(i)-f(j)=f(i/j) ,这个很显然,因为实际上 f f 函数的计算就是分解质因数,然后按照次数和好坏统计贡献,证明就免了,十分显然。

所以预处理出所有前缀 g c d gcd f f 函数值,然后倒过来贪心,如果除以当前前缀的 g c d gcd 能够获得正的贡献,那就直接除,不然放弃。维护一下当前已经除了的前缀 g c d gcd f f 值就可以统计答案了。


代码(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;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/83450815