版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011528035/article/details/70941621
题目:将这个数列分割成m段,每一段里面数的种类不超过k,现在询问当 k在[1,n]之间时,对应的m去最小,分别是多少?
思路:首先计算所有的集合数量到底有多少 ?n+n/2+n/3+n/4+n/5.....最后发现实际上集合并不多,而且上一轮的集合可以拿到当前回合利用,比如这一轮的第一个集合一定比上一回合的第一个集合大,而且多的那个数是现在的第一个集合是原来的第一个集合从第二个集合里面拿走的,那么维护一下第二个集合现在有多少个相同的数,再直接从第三个集合里面拿,所以我们可以直接跳到第三个集合之中去挑数。
如果重复的数多那么集合数量少,很快就会结束,反之我们可以直接跳跃区间(只要记录每个集合在上一轮最后一个数的位置),也很快得到答案。
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <stack>
using namespace std;
#define MAXA 100005
int v[MAXA];
int n;
struct node
{
int num;
int pre;
int nxt;
};
node rr[MAXA];
int col[MAXA];
int vol[MAXA];
int mq[MAXA][2];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&rr[i].num);
rr[i].pre=v[rr[i].num];
v[rr[i].num]=i;
col[i]=i;
vol[i]=1;
mq[i][1]=i;
}
for(int i=n;i>=1;i--)
{
rr[i].nxt=v[rr[i].num];
v[rr[i].num]=i;
}
for(int i=1;i<=n;i++)
{
int j=col[1]+1,k=1,st=1,h=i&1,l=2;
int now=mq[1][h];
int nxt=mq[2][h];
mq[k][h^1]=now;
while(j<=n)
{
if(rr[j].pre>=st)
{
col[now]++;
if(rr[j].nxt>col[nxt]||rr[j].nxt==j)
{
vol[nxt]--;
if(vol[nxt]==0)
nxt=mq[++l][h];
}
j++;
}
else if(vol[now]<i)
{
vol[now]++;
col[now]++;
if(rr[j].nxt>col[nxt]||rr[j].nxt==j)
{
vol[nxt]--;
if(vol[nxt]==0)
nxt=mq[++l][h];
}
j++;
}
else
{
st=col[now]+1;
j=col[nxt]+1;
now=nxt;
nxt=mq[++l][h];
mq[++k][h^1]=now;
if(j>n)break;
}
}
printf("%d ",k);
}
return 0;
}