洛谷 2564 题解

题意简述

给定nnn(&lt;=1e6&lt;=1e6<=1e6)个珠子,分为kkk个种类。接下来kkk行每行一个tit_iti(保证tit_iti的和=n=n=n),表示这种珠子有tit_iti个。然后是tit_iti个非负整数,表示这种珠子出现的位置(位置&lt;=1e9&lt;=1e9<=1e9)。
请你找一段区间,使得它包含所有类型的珠子并且长度最小。

数据

输入:
6 3
1 5
2 1 7
3 1 3 8
输出:
3

解释:

blog1.png
红色:第一类
蓝色:第二类
绿色:第三类
有多种方案可选,其中比较短的是1~ 5和5 ~8。后者长度为3最短。

思路

woc珠子位置怎么这么大。。。所以这告诉我们要打一个结构体存下所有珠子,然后按位置进行一次排序。
然后才进行暴力。枚举一个lll,表示我们现在考虑的珠子是排序后的第lll个。然后我们找一个rrrO(n)O(n)O(n)暴力找),使得从lllrrr之间包含所有类型的珠子,然后取一下最小值保存到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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值