题意
一个长度为 \(n\) 的序列,分为 \(k\) 段,每段代价是 \(c(l,r)=\sum\limits_{i=l}^r \sum\limits_{j=i}^r [\gcd(i,j)\geq l]\),求总代价的最小值。
分析
若 \(2^k>n\),总代价的最小值一定是 \(n\),因为此时最优分配方案就是让第 \(i\) 段的左端点是 \(2^{i-1}\),可以发现这样每一段中只有形如 \((x,x)\) 的数对才会对答案有贡献,因此总代价为 \(n\)。
对于其他的情况,设 \(dp_{i,j}\) 为分了 \(i\) 段,最后一段右端点为 \(j\) 的最小总代价,转移方程为:
\[dp_{i,j}=\min\limits_{k=1}^{j-1}\{dp_{i-1,k}+c(k+1,j)\} \]大力推式子:
\[c(l,r)=\sum\limits_{i=l}^r \sum\limits_{j=i}^r [\gcd(i,j)\leq l]=\sum\limits_{k=l}^r\sum\limits_{i=l}^r \sum\limits_{j=i}^r [\gcd(i,j)=k]=\sum\limits_{k=l}^r\sum\limits_{i=1}^{\left\lfloor\frac{r}{k}\right\rfloor} \varphi(i) \],此时可以先预处理出欧拉函数并计算前缀和,然后暴力转移,时间复杂度 \(\mathcal{O}(n^2+n\sqrt{n})\),显然会 T,考虑优化,由于是个分段的形式,因此猜测有决策单调性,只需证明 \(c\) 函数满足四边形不等式,即证 \(c(i,k)+c(j,l)\leq c(i,l)+c(j,k)\),其中 \(i\leq j\leq k \leq l\)。
证明:
设 \(f(i,j,r)=\sum\limits_{k=i}^j\sum\limits_{t=1}^{\left\lfloor\frac{r}{k}\right\rfloor} \varphi(t)\),则
\[\begin{gather} c(i,k)+c(j,l)\leq c(i,l)+c(j,k) \\ \Longleftrightarrow c(i,k)+c(j,l)\leq f(i,l,l)+f(j,k,k) \\ \Longleftrightarrow f(i,j-1,l)+f(j,l,l)+f(i,k,k)-f(i,j-1,k) \\ \Longleftrightarrow c(i,k)+c(j,l)\leq c(i,k)+c(j,l)+f(i,j-1,l)-f(i,j-1,k) \\ \Longleftrightarrow f(i,j-1,k)\leq f(i,j-1,l) \end{gather} \],又因为 \(k\leq l\),所以显然成立。
观察转移方程,发现每次转移都只从上层转移,又有决策单调性,因此可以分治优化 dp,通过分治确定每个点的最优决策点的区间,而预处理一个 \(\varphi\) 的前缀和,就能在短时间内快速从相邻的转移,每次从左侧加入或删除一个数就直接加上它的贡献,从右侧加入或删除则需要重新计算代价函数。预处理出来每次输出结果即可。
核心代码
int T,n,m,pri[MAXN],tot,phi[MAXN],pre[MAXN],L=1,R,res;
int dp[MAXM][MAXN];bitset<MAXN>vis;vector<int>v[MAXN];
void sieve(int sz){
pre[1]=vis[1]=phi[1]=1;
for(int i=2;i<=sz;i++){
if(!vis[i]) pri[++tot]=i,phi[i]=i-1;
for(int j=1;j<=tot&&i*pri[j]<=sz;j++){
vis[pri[j]*i]=true;
if(i%pri[j]) phi[pri[j]*i]=phi[i]*phi[pri[j]];
else{phi[pri[j]*i]=phi[i]*pri[j];break;}
}pre[i]=pre[i-1]+phi[i];
}
for(int i=1;i<=sz;i++) for(int j=i;j<=sz;j+=i) v[j].push_back(i);
}
int cal(int l,int r){
while(l<L) L--,res+=pre[R/L];
while(r>R){R++;for(auto i:v[R]) if(i>=L) res+=phi[R/i];}
while(l>L) res-=pre[R/L],L++;
while(r<R){for(auto i:v[R]) if(i>=L) res-=phi[R/i];R--;}return res;
}
void solve(int l,int r,int pl,int pr,int k){
if(l>r) return;int mid=(l+r)>>1,p=0,up=qmin(pr,mid-1),val;dp[k][mid]=inf;
for(int i=pl;i<=up;i++)
if(dp[k-1][i]+(val=cal(i+1,mid))<dp[k][mid]) dp[k][mid]=val+dp[k-1][i],p=i;
solve(l,mid-1,pl,p,k);solve(mid+1,r,p,pr,k);
}
signed main(){
int i,j,k;n=MAXN-5;m=17;sieve(n+2);
for(i=1;i<=n;i++) dp[1][i]=cal(1,i);
for(i=2;i<=m;i++) solve(1,n,0,n-1,i);
qread(T);
while(T--){
qread(n,m);
if(m>=17) printf("%lld\n",n);
else printf("%lld\n",dp[m][n]);
}return 0;
}
标签:limits,int,题解,sum,Partition,CF1603D,leq,MAXN,dp
From: https://www.cnblogs.com/lxy-2022/p/CF1603D-Solution.html