「CF226B」Naughty Stone Piles
题目简介
在你的面前有 \(n\) 堆石子从左往右排成一排,初始时每堆石子的大小(石子个数)依次为 \(a_1, a_2, \ldots, a_n\)。
你需要将这些石子进行合并。
每一步操作,你可以选择一堆石子并将这堆石子加入到另一堆石子中。如果你将第 \(i\) 堆石子加入到第 \(j\) 堆石子中,则第 \(j\) 堆石子的大小将会增加第 \(i\) 堆石子的大小,同时第 \(i\) 堆石子将会消失 \(\Rightarrow\) 我们将这样一步操作称为:将第 \(i\) 堆石子合并到第 \(j\) 堆石子中,其对应的代价为合并前第 \(i\) 堆石子的大小。
你的任务是确定将所有石子合并成一堆所需的最小代价。
但是本题中有一个限制:对于任意一堆石子,你将其它石子合并到这堆石子的次数不能超过 \(k\) 次。
有 \(q\) 次询问,每次询问都会给出一个整数 \(k\),你需要根据 \(k\) 的值确定将 \(n\) 堆石子合并成一堆的最小总代价(注:可能存在几次询问的 \(k\) 相同)。
分析
可以将合并过程看成一个 \(k\) 叉树不断向上合并。
只需要建立一个 \(n\) 个点的 \(k\) 叉树,然后从大到小按层填好相应权值,然后计算(略微有点哈夫曼树的味道?)
第 \(0\) 层会有 \(1\) 个节点,不会被移动;
第 \(1\) 层会有 \(k\) 个节点,会被移动 \(1\) 次;
第 \(2\) 层会有 \(k^2\) 个节点,会被移动 \(2\) 次;
\(\dots\)
然后就很简单了,不需要将树具体建出来。
\(AC\ Code\)
#include<algorithm>
#include<cstdio>
#include<iostream>
using namespace std;
using ll=long long;
const int Maxn=1e5+5;
int a[Maxn];
ll d[Maxn],c[Maxn];
int main(){
int n;cin>>n;
for(int i=1;i<=n;++i)cin>>a[i];
sort(a+1,a+n+1,greater<int>());
for(int i=1;i<=n;++i)d[i]=d[i-1]+a[i];
for(int i=1;i<=n;++i){
ll len=i;
for(int j=1,l=2,r;l<=n;++j,len*=i,l=r+1){
r=min(l+len-1,(ll)n);
c[i]+=(d[r]-d[l-1])*j;
}
}
int q;cin>>q;
while(q--){
int k;cin>>k;k=min(k,n);
cout<<c[k]<<' ';
}
return 0;
}

浙公网安备 33010602011771号