每日一题 7.22 CF.Till I Collapse [二分 分块]

Till I Collapse

将n个数划分成连续的m段使得每段中不同数字的个数≤k,对于每个k满足1≤k≤n求出最小的m。

分析一下容易发现,k的增大使得答案不增大,即答案具有单调性
其次,对于k答案不超过n/k,那么不同答案的个数不超过 n \sqrt{n} n 级别。
于是对于 n \sqrt{n} n 以内的我们直接暴力找, n \sqrt{n} n 以外的找到之外再去二分一块答案相同的部分。
此题略有分块思想,而具体实现则是二分。这里用的是双闭二分。

code

const ll maxn=1e5+5;
ll a[maxn];
bool c[maxn];
ll len;
ll n;
ll work(ll k)
{
	memset(c,0,sizeof c);
	ll ans=0,cnt=0,last=1;
	for(ll i=1;i<=n;i++){
		if(!c[a[i]])c[a[i]]=1,cnt++;
		if(cnt>k){
			ans++,cnt=1;
			for(ll j=last;j<i;j++)c[a[j]]=0;
			c[a[i]]=1;last=i;
		}
	}
	if(cnt)ans++;
	return ans;
}
int main()
{
	cin>>n;
	for(ll i=1;i<=n;i++)cin>>a[i];
	len=sqrt(n);
	for(ll i=1;i<=n;i++){
		if(i<=len){
			cout<<work(i)<<' ';
		}
		else {
			ll ans=work(i);
			ll l=i,r=n,mid;
			while(l<=r){
				mid=(l+r)>>1;
				if(work(mid)<ans)r=mid-1;
				else l=mid+1;
			}
			for(ll j=i;j<l;j++)cout<<ans<<' ';
			i=l-1;
		}
	}
	cout<<'\n';
	//system("pause");
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值