[CF1519C]Berland Regional

Berland Regional

题解

首先,每个大学本身对答案的贡献是独立的,所以我们可以单独对答案进行计算。
由于当参赛人数大于大学的总人数时,该大学不会对这些答案产生任何贡献,所以对于大学 u i u_{i} ui,我们只需要计算 [ 1 , ∣ U i ∣ ] [1,|U_{i}|] [1,Ui]的答案。
计算单个大学的答案时,我们可以先将该大学的人按 s s s排序,对于 i i i,我们会让前 n − n % i n-n\%i nn%i个人参加队伍,所以也就是 n − n % i n-n\%i nn%i的前缀和。

对于每个大学都计算一次它对答案产生的贡献,总时间复杂度 O ( ∑ i = 1 n ∣ U i ∣ l o g   ∣ U i ∣ ) = O ( n ) O\left(\sum_{i=1}^{n}|U_{i}|log\,|U_{i}|\right)=O\left(n\right) O(i=1nUilogUi)=O(n)

然后是另外一种SYDevil巨佬提供的一种 O ( n n ) O\left(n\sqrt{n}\right) O(nn )的做法,准确说就是当你忘记了对于每个大学只统计到 ∣ S ∣ |S| S时,可以将这些大学按人数分块,小块就统计到 n \sqrt{n} n ,大块统计到 n n n,其实也没什么特别的

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define lowbit(x) (x&-x)
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef unsigned int uint;
const int INF=0x7f7f7f7f;
const int jzm=233;
const int mo=998244353;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int t,n,ui[MAXN],len[MAXN];LL ans[MAXN];
vector<LL>uni[MAXN];
bool cmp(int x,int y){return x>y;}
int main(){
	read(t);
	while(t--){
		read(n);for(int i=1;i<=n;i++)read(ui[i]);
		for(int i=1,x;i<=n;i++)read(x),uni[ui[i]].push_back(1ll*x);
		for(int i=1;i<=n;i++){
			sort(uni[i].begin(),uni[i].end(),cmp);
			len[i]=uni[i].size();for(int j=1;j<len[i];j++)uni[i][j]+=uni[i][j-1];
			for(int j=1;j<=len[i];j++){
				int tmp=len[i]-len[i]%j;
				ans[j]+=uni[i][tmp-1];
			}
		}
		for(int i=1;i<=n;i++)printf("%lld ",ans[i]),ans[i]=0;puts("");
		for(int i=1;i<=n;i++)uni[i].clear();
	}
	return 0;
}

分块

int s,fr[200005],B;
vector<ll>a[200005];
vector<int>h,p;
int main(){
	for(int T=read;T--;){
		s=read;B=sqrt(s)+1;
		for(int i=1;i<=s;++i)
			a[i].clear();
		h.clear();p.clear();
		for(int i=1;i<=s;++i)fr[i]=read;
		for(int i=1;i<=s;++i)a[fr[i]].push_back(read);
		for(int i=1;i<=s;++i){
			if(a[i].size()>B)h.push_back(i);
			else p.push_back(i);
			sort(a[i].begin(),a[i].end());
			reverse(a[i].begin(),a[i].end());
			for(int j=1;j<a[i].size();++j)
				a[i][j]+=a[i][j-1];
		}
		for(int i=1;i<=s;++i){
			ll t=0;
			for(int j=0;j<h.size();++j)
				t+=a[h[j]].size()<i?0:a[h[j]][a[h[j]].size()/i*i-1];
			if(i<=B)
				for(int j=0;j<p.size();++j)
					t+=a[p[j]].size()<i?0:a[p[j]][a[p[j]].size()/i*i-1];
			printf("%lld ",t);
		}putchar('\n');
	}
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值