CF1603D题解
题意
记
数据范围:
solution
一个暴力的 DP 做法是:设
边界:
询问数很多,可以预处理出 DP 数组
DP状态数是
状态数没有太大的优化空间了,考虑优化
记
预处理出
考虑优化 DP 转移。根据这个DP分段的形式,大胆猜测
设
设
显然
剩下的部分可以使用1D1D决策单调性优化DP的常见套路(分治/单调队列)进行,可以参考本蒟蒻的博客。因为有
实际上,采用分治做法,假设当前的转移区间是
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5,LOG=18;
inline int read(){
int s=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
return s*f;
}
#define ll long long
const ll inf=1e18;
int prime[N],pcnt;
ll phi[N];
bool vis[N];
void init(int n){
phi[1]=1;
for(int i=2;i<=n;++i){
if(!vis[i]){
prime[++pcnt]=i;
phi[i]=i-1;
}
for(int j=1;j<=pcnt&&prime[j]*i<=n;++j){
vis[prime[j]*i]=1;
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
for(int i=1;i<=n;++i)phi[i]+=phi[i-1];
}
inline ll calc(int l,int r){
ll ret=0;
for(int i;l<=r;l=i+1){
i=min(r/(r/l),r);
ret+=1ll*phi[r/l]*(i-l+1);
}
return ret;
}
inline ll c(int l,int r){return phi[r/l];}
int T,n,k;
ll f[N][LOG];
void solve(int l,int r,int L,int R){
if(l>r)return;
int mid=(l+r)>>1;ll sum=calc(R+1,mid);
int pos=0;ll mn=inf;
for(int i=min(mid,R);i>=L;--i){
sum+=phi[mid/i];
if(sum+f[i-1][k-1]<=mn){
mn=sum+f[i-1][k-1];
pos=i;
}
}
f[mid][k]=mn;
solve(l,mid-1,L,pos);
solve(mid+1,r,pos,R);
}
int main(){
n=100000;init(n);
for(int i=1;i<=n;++i)f[i][1]=(1ll*i*(i+1))>>1;
for(k=2;k<=17;++k)
solve(1,n,1,n);
T=read();
while(T--){
n=read();k=read();
if(k>=20||n<(1<<k)){
printf("%d\n",n);
continue;
}
printf("%lld\n",f[n][k]);
}
return 0;
}