[题解]CF 1519C Berland Regional

[题解]CF 1519C Berland Regional

题意:有很多大学,每个大学有很多学生。每个大学需要组队,组队长度为k,如果人数小于k就无法组队。总的贡献为所有组队选手的能力值之和。现在要统计每个k的总贡献。
这道题唯一涉及到的算法就是一个前缀和优化而已。。。
真正优化的点,在于改变枚举的对象stl的使用
对于每个大学,我们按升序排序,然后计算出其前缀和,那么对于 k k k,此大学产生的贡献为 s u m [ ⌊ n u m [ i ] k ⌋ ∗ k ] sum\left[\left \lfloor \frac{num[i]}{k} \right \rfloor*k \right] sum[knum[i]k],其中 n u m [ i ] num[i] num[i]为第i个大学的人数。
如果我们枚举k,然后对于每个k,计算每个学校的贡献,这种方法很浪费时间。因为,如果一个学校人非常非常多,而其他学校只有一两个人,那么当 k > 2 k>2 k>2时就严重浪费了。也就是说,对于不同的学校,有用的 k k k的上限不同。
所以,如果我们枚举学校,然后对于每个学校,分别按照k计算出贡献,然后存到一个答案数组里面,这样的时间复杂度就会少很多。
注意到,对于每个学校,我们枚举k的上限就是学生的人数,所以时间复杂度为 O ( n ) O(n) O(n)
再说说stl的使用。其实stl是非常容易超时的。有个好办法是开 O 2 O2 O2优化。虽然辣鸡cf不能用
这里注意,vector如果是在函数内定义,最好指明长度。不然会像我一样T成啥🐕

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdio>
#include<set>
#include<stack>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int N = 2e5 + 100;
ll u[N];
ll s[N];
ll pre[N];
ll outp[N];
vector<ll> ans[N];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
    int t;
    cin>>t;
	while(t--){
	    memset(outp, 0, sizeof outp);
		int n;
		cin>>n;
		set<ll> ss;//set不会存储重复值,用其来存储出现的学校有哪些
		for (int i = 1; i <= n;i++){
			ans[i].clear();
			cin>>u[i];
			ss.insert(u[i]);
		}
		for (int i = 1; i <= n;i++){
			cin>>s[i];
			ans[u[i]].push_back(s[i]);
		}
		for(int e : ss){//学校和学校相互独立;如果枚举k的话,有些学校的人数不会到达k,造成很多浪费。
			sort(ans[e].begin(), ans[e].end(), greater<ll>());//按降序排
			int num = ans[e].size();
			for (int i = 1; i <= num;i++){
				pre[i] = pre[i - 1] + ans[e][i - 1];//存储每个学校的和
			}
			for (int i = 1; i <= num;i++){
				outp[i] += pre[num / i * i];//存储所有学校的和
			}
		}
		for (int i = 1; i <= n;i++){
			cout << outp[i] << ' ';
		}
		cout << endl;
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值