ABC143 F Distinct Numbers 刁钻神奇数论

题目链接

神奇数论

这题公式有点神奇。
首先统计给的数,计算不同种类的数每个数的个数,把统计结果放到数组 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(Xk=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
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;
}

你可能感兴趣的:(程序竞赛)