D1. Divan and Kostomuksha (easy version)

dpidp_i表示把gcd{\gcd}i{i}的一组数放到前面的贡献,cnti{cnt_i}表示含有因子i{i}的数的个数。

状态转移方程有:dpi=max(dpi,dpd+cnti(id)),[di]{dp_i=max(dp_i,dp_d+cnt_i*(i-d))},[d|i],可以直接用调和级数转移O(NlogN){O(N\log N)}

cnti{cnt_i}可以直接用调和级数O(NlogN){O(N\log N)}

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=5e6+7,mod=1e9+7;
ll f[maxn],res;
int cnt[maxn];
int main() {
	cin.sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	int n;
	cin>>n;
	for(int i=1,x;i<=n;++i) {
		cin>>x;
		++cnt[x];
	}
	for(int i=1;i<=5e6;++i) {
		for(int j=i+i;j<=5e6;j+=i) cnt[i]+=cnt[j];
	}
	f[1]=n;
	for(int i=1;i<=5e6;++i) {
		res=max(res,f[i]);
		for(int j=i+i;j<=5e6;j+=i) f[j]=max(f[j],f[i]+1LL*cnt[j]*(j-i));
	}
	cout<<res<<'\n';
	return 0;
}

D2. Divan and Kostomuksha (hard version)

直接交D1{D1}代码会T,(主要是状态转移的那个O(NlogN){O(N\log N)}里的dpi{dp_i}开的是long long{long\ long},有常数)。

可以用类似线性筛的写法O(N){O(N)}cnti{cnt_i}.

for(int p:prime) {
	for(int i=M/p;i;--i) cnt[i]+=cnt[i*p];
}

x=p1p2p3,y=p3{x=p_1*p_2*p_3,y=p_3}cnty{cnt_y}累加cntx{cnt_x}的过程:

cntp2p3+=cntx{cnt_{p_2*p_3}+=cnt_x}

cntp3+=cntp1p3{cnt_{p_3}+=cnt_{p_1*p_3}}

cntp1p3+=cntx{cnt_{p_1*p_3}+=cnt_x}

cntp3+=cntp2p3{cnt_{p_3}+=cnt_{p_2*p_3}}

保证不会漏算或者多算(优化不大)。

对于状态转移来说,每次只转移质数倍是最优的。即dp1>dpp1>dpp1p2{dp_1->dp_{p_1}->dp_{p_1*p_2}}优于dp1>dpp1p2{dp_1->dp_{p_1*p_2}}

dp1+(cntp1cntp1p2)(p11)+cntp1p2(p1p21)>=dp1+cntp1p2(p1p21){dp_1+(cnt_{p_1}-cnt_{p_1*p_2})*(p_1-1)+cnt_{p_1*p_2}*(p_1*p_2-1)>=dp_1+cnt_{p_1*p_2}*(p_1*p_2-1)}

所以可以枚举素数转移,素数的数量logN{\log N},复杂度好像是O(NloglogN){O(N\log \log N)}

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=2e7+7,M=2e7,mod=1e9+7;
vector<int>prime;
bool vis[maxn];
inline void init() {
	for(int i=2; i<maxn; ++i) {
		if(!vis[i]) prime.emplace_back(i);
		for(auto j:prime) {
			if(i*j>=maxn) break;
			vis[i*j]=1;
			if(i%j==0) break;
		}
	}
}
ll f[maxn],res;
int cnt[maxn];
int main() {
	init();
	cin.sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	int n;
	cin>>n;
	for(int i=1,x;i<=n;++i) {
		cin>>x;
		++cnt[x];
	}
	for(int p:prime) {
		for(int i=M/p;i;--i) cnt[i]+=cnt[i*p];
	}
	f[1]=n;
	for(int i=1;i<=M;++i) {
		res=max(res,f[i]);
		for(int p:prime) {
			if(i*p>M) break;
			f[i*p]=max(f[i*p],f[i]+1LL*cnt[i*p]*i*(p-1));
		}
	}
	cout<<res<<'\n';
	return 0;
}