AtCoder Beginner Contest 143 F - Distinct Numbers

这道题是一道灰常玄学的题。
我们要对它进行一个抽象的模型建立
首先确定一点:只要维护相同数的个数,无论数列中的数是多少都没有问题。
然后显而易见地有一个贪心删法:每次取个数最多的数删除
举个例子:对于数列1 2 2 3 3 3 4 4 4 4
我们建立一个 a a a数组, a i a_i ai记录 i i i的个数
a = { 0 , 1 , 2 , 3 , 4 } a=\{0,1,2,3,4\} a={0,1,2,3,4}
我们将 a a a变成一张直方图:
AtCoder Beginner Contest 143 F - Distinct Numbers_第1张图片
k = 3 k=3 k=3,那么每次我们删除便相当于将去掉三个柱子的最上面一层
AtCoder Beginner Contest 143 F - Distinct Numbers_第2张图片
想象一下这个直方图是一层一层的地毯叠成的,而每一层地毯长度为 k k k
我们要求的就是有多少层地毯。
比如说这样的一幅图:
AtCoder Beginner Contest 143 F - Distinct Numbers_第3张图片
k = 6 k=6 k=6时,可以分解为这样的情况:
AtCoder Beginner Contest 143 F - Distinct Numbers_第4张图片
k = 6 k=6 k=6时,答案为4
接下来我们要做的,就是探究地毯层数与方块数之间的关系。

我们设 k k k相应的答案为 i i i
意味着这张图中可以抽走 i i i层长度为 k k k的地毯
显而易见:无论一根柱子比 i i i高多少,更高的格子都无法对答案造成贡献:因为我们只抽 i i i次,而且不会一次抽两层
故可以乱搞出证明:设 c n t i cnt_i cnti为最底下 i i i层的格子个数,则 c n t i ≥ i k cnt_i\ge ik cntiik是对于每一次删 k k k个数答案为 i i i的必要条件。

那么如何证明它是充分条件呢?
首先,易见
AtCoder Beginner Contest 143 F - Distinct Numbers_第5张图片
这样的一个图形一定可以得到 i i i的结果
而对于
AtCoder Beginner Contest 143 F - Distinct Numbers_第6张图片
这样的一般性图形
我们将高的柱子移动到中间,然后进行填补即可
AtCoder Beginner Contest 143 F - Distinct Numbers_第7张图片
证明:总能找到一种方案,使得每一行一定没有重复的数字
嘿嘿,这个证明我们不能用填补法了
引用HYY奆老的证明(我讲了半个小时愣是没给远哥讲懂他一句话就搞定了
我们直接把 i i i下面的部分截为一个单独的一维数组,然后按顺序排下来就好了
AtCoder Beginner Contest 143 F - Distinct Numbers_第8张图片
对于这个数组 k = 4 , i = 5 k=4,i=5 k=4,i=5的情况:
我们把下面5层拉出来,然后补成这样:
AtCoder Beginner Contest 143 F - Distinct Numbers_第9张图片
同一框内数相同
因为每个数的个数一定 ≤ i \le i i,故不可能有重复
(HYY太巨了

接下来就是更加玄学的代码
先贴一个完整的

#include
#include
using namespace std;
int a[1110000];
long long cnt[1110000],ans[1110000];
bool cmp(int a,int b)
{return a>b;}
inline void read(int &x)
{
	x=0;register char c=getchar();
	while(c<48||57<c)c=getchar();
	for(;48<=c&&c<=57;c=getchar()) x=x*10+(c&15);
}
void write(const int x)
{
	if(x>9)write(x/10);
	putchar(x%10|48);
}
int main()
{
	int n;
	read(n);
	int x;
	for(int i=1;i<=n;i++)
	{
		read(x);
		a[x]++;
	}
	//a:the number of the xs
	for(int i=1;i<=n;i++)
		cnt[a[i]]++;
	//cnt:the number of the as
	
	for(int i=n;i;i--)
		cnt[i]+=cnt[i+1];
	int Ans=0;
	long long Cnt=0;
	for(int i=n;i;i--)
	{
		while((Cnt+cnt[Ans+1])>=1ll*i*(Ans+1))
			Cnt+=cnt[++Ans];
		ans[i]=Ans;
	}
	for(int i=1;i<=n;i++)
		write(ans[i]),putchar(10);
	return 0;
}

(师兄考试时卡了IO)
首先,我们作出 a a a数组
然后我们用 c n t i cnt_i cnti记录 a a a中高度为 i i i的柱状图的个数

	for(int i=1;i<=n;i++)
		cnt[a[i]]++;

求一次后缀和得到 c n t i cnt_i cnti为高度至少有 i i i的柱状图个数

	for(int i=n;i;i--)
		cnt[i]+=cnt[i+1];

此时从每一层来看, c n t i cnt_i cnti就是第 i i i层有多少个格子

然后用 C n t Cnt Cnt一层一层加,得到第 A n s Ans Ans层以下有多少个格子

	for(int i=n;i;i--)
	{
		while((Cnt+cnt[Ans+1])>=1ll*i*(Ans+1))
			Cnt+=cnt[++Ans];
		ans[i]=Ans;
	}

其中for循环中的 i i i就是题目中所说的 k k k

由于答案一定是随 k k k递减单调增的,而且一定不会超过 n n n
所以整体复杂度为 O ( n ) O(n) O(n)

你可能感兴趣的:(做题笔记)