「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;
}

$$-----EOF-----$$

posted @ 2023-03-14 11:50  AlienCollapsar  阅读(26)  评论(0)    收藏  举报
// 生成目录索引列表 // ref: http://www.cnblogs.com/wangqiguo/p/4355032.html // modified by: zzq