CF1603D Artistic Partition 题解

题意

一个长度为 \(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;
}

record

posted @ 2022-09-28 08:09  l_x_y  阅读(32)  评论(0编辑  收藏  举报