http://acm.hdu.edu.cn/showproblem.php?pid=6533
题目描述
构建一个m层的满n叉树,所有的边都有权值。
每条边的权值从k个数里面选,每个数只能被选一次
0是根节点。di表示根节点到i点的距离,我们的目标是使得每个点到根节点的和最小。
答案可能很大,请输出取模p后的答案
输入
输入包含两行
第一行四个数,k,m,n,p (2<=k<=2e5 , 2<=p<=1e15)
第二行包含k个数,保证k大于等于边数m,k个数每个数字保证小于等于1e15
输出
答案包含一个数字表示所有点到根节点的距离之和。
样例输入
5 2 3 10 1 2 3 4 5
样例输出
6
思路:先求出m层的满n叉树有多少条边(或者节点),除去根节点每个点看作是一条边,保存每个节点和父节点的累加和,类似前缀和一样加起来。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
ll ans = 0;
ll a[200007], b[200007];
int main(void) {
ll k, m, n, p;
while(~scanf ("%lld %lld %lld %lld", &k, &m, &n, &p)) {
ans = 0;
for (int i = 1; i <= k; ++i) {
scanf ("%lld", &a[i]);
}
sort(a+1, a+k+1);
ll N;
if(n == 1) N = m; //如果是一条链(一叉树)
else N = (pow(n, m) - 1) / (n - 1); ///m层的n叉树的全部节点个数
b[0] = 0;
for (int i = 1; i < N; ++i) { //除去根节点 共N-1个节点
b[i] = ((b[(i-1) / n]) % p + a[i] % p) % p; //当前节点到父节点的距离加上父节点到根节点的距离
ans = (ans + b[i]) % p;
}
printf("%lld\n", ans);
}
return 0;
}