前言
没有困难创造困难也要上!
题目
题目大意:
\(t\) 组数据,每组数据有 \(n\) 个学生。
第一行输入每个学生所属大学 \(u_i\),第二行输入每个学生的能力值 \(s_i\)。
现有一场比赛,一个队伍中的人必须是同一个学校,且人数恰好为 \(k\),每个学校会最大化比赛的学生的能力值。一场比赛的精彩程度为出战学生的能力值之和。
对于每一个 \(k\in[1,n]\),求出该比赛的精彩程度。
\(1\le t\le 1000;1\le n\le 2\cdot 10^5;1\le u_i\le n;1\le s_i\le 10^9;\sum n\le 2\cdot 10^5.\)
讲解
part1 错误思路
先写一下虚拟赛时的错误思路以提醒自己,想直接看正解的读者可跳过。
首先我们将每个大学的学生都整理出来并按能力值从大到小排序没问题,用不用 \(\tt vector\) 都无所谓。
然后直接从 \(1\) 到 \(n\) 枚举 \(k\)。
如果一个属于学校 \(i\) 的人 \(j\) 存在一个 \(w\in[0,\frac{n}{k}]\)满足:\(w*k<rk_{(i,j)}\le(w+1)k\le siz_i\),那么这个人可以被 \(k\) 统计进去。
为什么我会写一个这么复杂的不等式出来啊!!!
做不出来应该果断换思路,也就是说换一下对题目条件的翻译。可惜头铁了。
甚至一度认为D比C简单得多。
上面那个式子好像可以用分块做。
part2 正解
对于每所大学 \(i\),对 \(k\) 的贡献即为前 \(n- n\mod k\) 的学生能力值之和。
如果它拥有 \(siz_i\) 个学生,它所能够提供贡献的最大的 \(k\) 即为 \(k=siz_i\) 时的情况,这减少了我们的工作量。
而每所大学的贡献显然是独立的,所以对于每所大学枚举 \(k\) ,然后用前缀和算贡献即可。
时间复杂度 \(O(\sum n\log_2n)\)。
代码
目前CF rk1。
提醒一下:\(\tt vector\) 挺慢的,如果没有 \(O2\) 的话。
struct node
{
int u,s;
bool operator < (const node &px)const{
if(u != px.u) return u < px.u;
return s > px.s;
}
}p[MAXN];
LL pre[MAXN],ans[MAXN];
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
for(int T = Read(); T ;-- T)
{
n = Read();
for(int i = 1;i <= n;++ i) p[i].u = Read();
for(int i = 1;i <= n;++ i) p[i].s = Read(),ans[i] = 0;
sort(p+1,p+n+1);
for(int i = 1;i <= n;++ i)
{
int l = i,r = i;
pre[1] = p[i].s;
while(r < n && p[r+1].u == p[l].u) r++,pre[r-l+1] = pre[r-l] + p[r].s;
int len = r-l+1;
for(int j = l;j <= r;++ j) ans[j-l+1] += pre[len - len % (j-l+1)];
i = r;
}
for(int i = 1;i <= n;++ i) Put(ans[i],' ');
putchar('\n');
}
return 0;
}