题目链接
神奇数论
这题公式有点神奇。
首先统计给的数,计算不同种类的数每个数的个数,把统计结果放到数组
C
n
C_n
Cn里面(下标范围:1~m),sort一下这个数组。然后反过来想:“如果我想取X次,那么这X次取数中每次取的不同数的个数Y的最大值是多少”
Y
=
f
(
X
)
=
f
l
o
o
r
(
∑
k
=
1
m
min
(
X
,
C
k
)
X
)
,
f
(
0
)
=
N
Y=f(X)=floor(\frac{\sum_{k=1}^{m}\min(X,C_k)}{X}),\quad f(0)=N
Y=f(X)=floor(X∑k=1mmin(X,Ck)),f(0)=N
最后再把答案数组的区间 A n s ( f ( X + 1 ) , f ( X ) ] Ans(f(X+1),f(X)] Ans(f(X+1),f(X)]赋值 X X X,输出答案数组即可。(上面的求和可以用前缀和优化复杂度,即:横着统计 C n C_n Cn数组)
考察
挺难
#include<bits/stdc++.h>
using namespace std;
//省略一些宏
//---------------------
#define MAXN 300005
//---------------------
ll n;
ll a[MAXN];
ll b[MAXN+1];
ll sum[MAXN+1];
ll ans[MAXN+1];
int main(){
cin >> n;
REP(i,n) cin >> a[i];
sort(a,a+n);
ll lastn = -1;
ll id = -1;
REP(i,n) if(lastn!=a[i]){id++;lastn = a[i];a[id] = 1;}else{a[id]++;}
ll m = id+1;
sort(a,a+m);
id = 0;
ZERO(b);
lastn = -1;
REP(i,m) if(a[i]!=lastn){
lastn = a[i];
while(id<a[i]){
id++;
b[id] = m-i;
}
}
ZERO(sum);
TOSUM(b,sum,(n+1));
ll f[MAXN+1];
ZERO(f);
REP1(i,n) f[i] = (ll)( ((double)sum[i]) / i );
ZERO(ans);
f[0] = n;
REP(i,n+1) FORE(j,f[i+1]+1,f[i]) ans[j] = i;
REP1(i,n) PRT(ans[i]);
return 0;
}