CF786C Till I Collapse

题目分析

首先,对于这道题,可以用贪心以一个\(O(n)\)的复杂度求解一个\(k\)的值

暴力是\(O(n^2)\)的复杂度,当然过不了。

我们手推一下样例,会发现,答案满足单调性,于是,果断想到二分。

再推一推性质,会发现,实际上,一个答案最多会出现\(\sqrt n\)次,于是,可以对答案分块,用二分枚举右边界,时间复杂度\(O(n\sqrt nlog(n))\),还是过不了。

但因为每一次一个答案最多会出现\(\sqrt n\)次,这就是块的最大长度。

于是,对于\(k\le\sqrt n\)的部分,可以暴力,最后,卡卡常就过了。

对于每一个数,可以记录出现的最大块,看是否在当前块出现,这样就避免了在算 \(k\)时多出一个清空数组的时间复杂度

#include<bits/stdc++.h>
using namespace std;
int n;
int a[100005];
int tot[100005]; 
int size;
int check(int mid)
{
	int now=1;
	int cout=0;
	for(int i=1;i<=n;i++)
	{
		if(tot[a[i]]!=now)
		{
			cout++;
			tot[a[i]]=now;
		}
		if(cout>mid)
		{
			cout=1;
			now++;
			tot[a[i]]=now;
		}
	}
	return now; 
}
int main()
{
	scanf("%d",&n);
	size=sqrt(n*log2(n));
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=size;i++)
	{
		int now=1;
		int cout=0;
		memset(tot,0,sizeof(tot));
		for(int j=1;j<=n;j++)
		{
			if(tot[a[j]]!=now)
			{
				cout++;
				tot[a[j]]=now;
			}
			if(cout>i)
			{
				cout=1;
				now++;
				tot[a[j]]=now;
			}
		}

		printf("%d ",now);
	}
	int l=size+1;
	int r;
	while(l<=n)
	{
		int now=1;
		int cout=0;
		memset(tot,0,sizeof(tot));
		for(int i=1;i<=n;i++)
		{
			if(tot[a[i]]!=now)
			{
				cout++;
				tot[a[i]]=now;
			}
			if(cout>l)
			{
				cout=1;
				now++;
				tot[a[i]]=now;
			}
		}
		int L=l;
		int R=n;
		while(L<=R)
		{
			int mid=(L+R)>>1;
			memset(tot,0,sizeof(tot));
			int tc=check(mid);
			if(tc>=now)
			{
				L=mid+1;
				r=mid;
			}
			else
			{
				R=mid-1;
			}
		}
		for(int i=l;i<=r;i++)
		{
			printf("%d ",now);
		}
		l=r+1;
	}
} 
posted @ 2021-07-20 11:54  kid_magic  阅读(51)  评论(0编辑  收藏  举报