D1. Divan and Kostomuksha (easy version)
记表示把为的一组数放到前面的贡献,表示含有因子的数的个数。
状态转移方程有:,可以直接用调和级数转移
求可以直接用调和级数
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)
直接交代码会T,(主要是状态转移的那个里的开的是,有常数)。
可以用类似线性筛的写法求.
for(int p:prime) {
for(int i=M/p;i;--i) cnt[i]+=cnt[i*p];
}
,累加的过程:
保证不会漏算或者多算(优化不大)。
对于状态转移来说,每次只转移质数倍是最优的。即优于:
所以可以枚举素数转移,素数的数量,复杂度好像是
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;
}