题意简述
给定nnn(<=1e6<=1e6<=1e6)个珠子,分为kkk个种类。接下来kkk行每行一个tit_iti(保证tit_iti的和=n=n=n),表示这种珠子有tit_iti个。然后是tit_iti个非负整数,表示这种珠子出现的位置(位置<=1e9<=1e9<=1e9)。
请你找一段区间,使得它包含所有类型的珠子并且长度最小。
数据
输入:
6 3
1 5
2 1 7
3 1 3 8
输出:
3
解释:

红色:第一类
蓝色:第二类
绿色:第三类
有多种方案可选,其中比较短的是1~ 5和5 ~8。后者长度为3最短。
思路
woc珠子位置怎么这么大。。。所以这告诉我们要打一个结构体存下所有珠子,然后按位置进行一次排序。
然后才进行暴力。枚举一个lll,表示我们现在考虑的珠子是排序后的第lll个。然后我们找一个rrr(O(n)O(n)O(n)暴力找),使得从lll到rrr之间包含所有类型的珠子,然后取一下最小值保存到ansansans。
这是O(n2)O(n^2)O(n2)的。但是,我们会发现。。。
每次lll往右移的时候,珠子是不会更多的,只会少。也就是说我们的rrr不会比上一次的rrr小,即rrr也是单调非降的。这个r好像是单调非降的,不如我们把它。。。吃了 记录下来,然后每次不断右移判断是否满足条件。
但现在问题来了,我们如何记录l++l++l++之后的剩余元素情况以及元素个数呢?
开一个cntcntcnt数组,cnt[i]cnt[i]cnt[i]记录种类iii出现了几次 ,还有一个totaltotaltotal记录有多少元素。
然后l++l++l++的时候,先把lll这个珠子上的cnt−−cnt--cnt−−,然后如果cnt==0cnt==0cnt==0,那么说明这个珠子没了,就要把totaltotaltotal也−−--−−。记录完之后再l++l++l++。
r++r++r++的时候类似,不过顺序不一样,是先r++r++r++,然后记录。(想想为什么)。
代码:
#include<bits/stdc++.h>
#define N 1001000
#define K 61
using namespace std;
struct node//珠子
{
int type,pos;
const bool operator<(const node CompWith) const//按位置排序
{
return pos<CompWith.pos;
}
}pearl[N];
int n,k;
int R;
void Input()
{
scanf("%d%d",&n,&k);
int p_pearl=0;//记录是总共多少个,由题意,到最后这个p_pearl应该=n
for(int i=1;i<=k;i++)
{
int t;scanf("%d",&t);
while(t--)
{
int pos;
scanf("%d",&pos);
pearl[++p_pearl]=(node){i,pos};
R=max(R,pos);//一开始想用的,后来发现没用。。。请忽略它。。。
}
}
}
int cnt[K],total;
void Solve()
{
memset(cnt,0,sizeof(cnt));
total=0;
sort(pearl+1,pearl+n+1);//不排序见比利了。。。
int l=1,r=0;//调了好久才发现这个问题。。。
while(r<=n and total<k)
{
++r;//r先加
//原因是l++的时候我们是要删除的是l,然后l++。在r++的时候,我们是要加入r+1,然后r++。所以还不如先r++然后处理,否则下面就要写成
/*
++cnt[pearl[r+1].type];
if (cnt[pearl[r+1].type]==1)
{
++total;
}
r++;
*/
//多麻烦。。。
++cnt[pearl[r].type];
if (cnt[pearl[r].type]==1)//说明r的这个种类是第一次出现,所以total++
{
++total;
}//再记录
}
int ans=pearl[r].pos-pearl[l].pos;
while(l<=n and r<=n)
{
ans=min(ans,pearl[r].pos-pearl[l].pos);//注意:不用+1,因为题目中说5和8的距离是3。。。
cnt[pearl[l].type]--;
if (cnt[pearl[l].type]==0)
{
total--;
}//先删除
l++;//再l++
while(total<k and r<=n)
{
++r;//先++r(++r和r++虽然有区别,但是在这里是一样的,先写++还是先写r按心情。。。)
cnt[pearl[r].type]++;
if (cnt[pearl[r].type]==1)
{
total++;
}//然后记录
}
if (r==n+1) break;//防爆
}
printf("%d\n",ans);
}
main()
{
Input();
Solve();
return 0;
}
1950

被折叠的 条评论
为什么被折叠?



