前缀和 + 暴力 + 排序 - Berland Regional - CF 1519C

前缀和 + 暴力 + 排序 - 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]mm×klenileni

所 以 , 我 们 对 每 个 集 合 先 从 大 到 小 进 行 排 序 。 所以,我们对每个集合先从大到小进行排序。

下 面 的 关 键 问 题 在 于 , 如 何 快 速 计 算 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\ 个数。  lenileni % j=jleni×j 

由 于 我 们 使 用 v e c t o r 来 存 储 , 下 标 从 0 开 始 , 所 以 最 后 的 下 标 还 要 相 应 − 1 。 由于我们使用vector来存储,下标从0开始,所以最后的下标还要相应-1。 使vector01

代码:

#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; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值