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

传送门


解析:

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

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

fff的实现多种多样,这里就不细讲,主要讲一下两种做法O(n2)O(n^2)O(n2)DPDPDPO(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|ijif(i)−f(j)=f(i/j)f(i)-f(j)=f(i/j)f(i)f(j)=f(i/j),这个很显然,因为实际上fff函数的计算就是分解质因数,然后按照次数和好坏统计贡献,证明就免了,十分显然。

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


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

转载于:https://www.cnblogs.com/zxyoi/p/10047154.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值