Colored Balls[CF-792E](分块)

23 篇文章 0 订阅
2 篇文章 0 订阅

题目

n n n 种颜色的球,第 i i i 种球有 a i a_i ai 个,让你把球分成几个集合。
要求:

  • 一个集合里的球只能有一种颜色。
  • 每两个集合的球的数量相差不能 > 1 >1 >1

让你求出这些球最少分几个集合。
1 ≤ N ≤ 500 , 1 ≤ a i ≤ 1 0 9 1\le N\le 500,1\le a_i\le 10^9 1N500,1ai109

思路

考虑当前每个集合大小为 s i z siz siz s i z + 1 siz+1 siz+1
显然 s i z siz siz 越大对我们越优
a i = n u m ⋅ s i z + t ( 0 ≤ t ≤ s i z − 1 ) a_i=num\cdot siz+t\quad (0\le t\le siz-1) ai=numsiz+t(0tsiz1)
那么要满足 n u m ≥ t num\ge t numt ,对于当前 a i a_i ai 是合法的 但不一定是最优的
但是 s i z siz siz 范围是 1 e 9 1e9 1e9 ,于是不能每个都 O ( n ) O(n) O(n) 检验,考虑分块:

  1. s i z ≤ n siz\le\sqrt{n} sizn 时,那么 n u m ≥ n ≥ s i z > t num\ge\sqrt{n}\ge siz>t numn siz>t,是合法的,我们就直接取 s i z = n siz=\sqrt{n} siz=n 让答案最大

  2. s i z > n siz> \sqrt{n} siz>n 时,那么 n u m < n num<\sqrt{n} num<n ,由于 s i z siz siz 太大,我们枚举 n u m num num
    应该满足 n u m ≥ t ≥ 0 num\ge t\ge 0 numt0,于是有 s i z = ⌊ a i n u m ⌋ siz=\lfloor\frac{a_i}{num}\rfloor siz=numai
    有一种额外情况: t = 0 t=0 t=0
    此时 a i a_i ai 一定为 n u m num num 的倍数,于是有 s i z = a i n u m − 1 siz=\frac{a_i}{num}-1 siz=numai1 也是合法的

考虑当 s i z > n siz>\sqrt{n} siz>n 时候我们只需要检验 M i n a Mina Mina 即可
如果我们从小到大枚举 n u m num num,在 n u m ≥ n num\ge \sqrt{n} numn 时候就会结束,总的次数在 n \sqrt{n} n 以内
考虑一组数据

2
1 100
51

证明不能按照上面的算

代码

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<climits>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long 
#define ULL unsigned long long 
int read(){
	bool f=0;int x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define MAXN 500
#define INF 0x3f3f3f3f
LL ans;
int n,a[MAXN+5];
bool check(int x){
	x++,ans=0;
	for(int i=1;i<=n;i++){
		int k=a[i]/x,t=a[i]%x;
		if(!t){
			ans+=k;
			continue;
		}
		if(k+t<x-1)
			return 0;
		ans+=k+1;
	}
	return 1;
}
int main(){
	n=read();
	int Low=INF;
	for(int i=1;i<=n;i++)
		a[i]=read(),Low=min(Low,a[i]);
	for(int i=1;i<=Low;i++)
		if(check(Low/i)||check(Low/i-1)){//这里其实在里面加的1
			printf("%lld\n",ans);
			return 0;
		}
	return 0;
}

思考

这种题思维量极大,平时一定要注意锻炼思维

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值