前缀和 + 暴力 + 排序 - Berland Regional - CF 1519C
分析:
说 到 底 , 本 题 是 一 个 要 求 快 速 计 算 区 间 和 的 问 题 。 说到底,本题是一个要求快速计算区间和的问题。 说到底,本题是一个要求快速计算区间和的问题。
根 据 题 意 , 每 次 需 要 对 每 个 集 合 前 m × j 大 的 数 进 行 求 和 , 根据题意,每次需要对每个集合前m×j大的数进行求和, 根据题意,每次需要对每个集合前m×j大的数进行求和,
其 中 j ∈ [ 1 , n ] , 是 分 组 的 长 度 ; m 是 满 足 m × k ≤ l e n i 的 最 大 的 正 整 数 ; l e n 是 第 i 个 集 合 的 大 小 。 其中j∈[1,n],是分组的长度;m是满足m×k\le len_i的最大的正整数;len是第i个集合的大小。 其中j∈[1,n],是分组的长度;m是满足m×k≤leni的最大的正整数;len是第i个集合的大小。
所 以 , 我 们 对 每 个 集 合 先 从 大 到 小 进 行 排 序 。 所以,我们对每个集合先从大到小进行排序。 所以,我们对每个集合先从大到小进行排序。
下 面 的 关 键 问 题 在 于 , 如 何 快 速 计 算 m × k 的 值 : 下面的关键问题在于,如何快速计算m×k的值: 下面的关键问题在于,如何快速计算m×k的值:
也 就 是 说 , 我 们 每 次 要 去 除 末 尾 的 , 模 k 余 下 来 的 几 个 数 。 也就是说,我们每次要去除末尾的,模k余下来的几个数。 也就是说,我们每次要去除末尾的,模k余下来的几个数。
也 就 是 前 l e n i − l e n i % j = ⌊ l e n i j ⌋ × j 个 数 。 也就是前\ len_i -len_i\ \%\ j=\lfloor\frac{len_i}{j}\rfloor×j\ 个数。 也就是前 leni−leni % j=⌊jleni⌋×j 个数。
由 于 我 们 使 用 v e c t o r 来 存 储 , 下 标 从 0 开 始 , 所 以 最 后 的 下 标 还 要 相 应 − 1 。 由于我们使用vector来存储,下标从0开始,所以最后的下标还要相应-1。 由于我们使用vector来存储,下标从0开始,所以最后的下标还要相应−1。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<unordered_map>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const int N = 200010;
int t, n, u[N], x;
vector<ll> g[N];
ll ans[N];
void init()
{
for(int i=1;i<=n;i++)
{
ans[i] = 0;
g[i].clear();
}
}
int main()
{
scanf("%d", &t);
while(t --)
{
scanf("%d", &n);
init();
for(int i=1;i<=n;i++) scanf("%d", &u[i]);
for(int i=1;i<=n;i++)
{
scanf("%d", &x);
g[u[i]].push_back(x);
}
//降序
for(int i=1;i<=n;i++)
sort(g[i].begin(), g[i].end(), greater<int>());
//前缀和
for(int i=1;i<=n;i++)
{
int m = g[i].size();
for(int j=1;j<m;j++)
g[i][j] += g[i][j-1];
}
//计算答案
for(int i=1;i<=n;i++)
{
int len = g[i].size();
for(int j=1;j<=len;j++)
ans[j] += g[i][len/j*j-1];
}
for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
puts("");
}
return 0;
}