洛谷 3069 [USACO13JAN]牛的阵容Cow Lineup 题解

博客详细解析了USACO比赛中的‘牛的阵容’问题,给出了数据输入、输出、样例,并探讨了如何在1e5规模数据下,通过线性枚举和尺取法解决这个问题,避免了二进制枚举的复杂性,同时利用STL中的map进行编号优化,以统计不同血统的牛数量。

题意简述

一个数组aaa中有n(n&lt;=1e5)n(n&lt;=1e5)n(n<=1e5)个数,a[i](a[i]&lt;=1e9)a[i](a[i]&lt;=1e9)a[i](a[i]<=1e9)表示第iii头牛的"血统编号"。请你帮Famer John删去其中所有的k(k&lt;=n)k(k&lt;=n)k(k<=n)种,使得剩下的牛血统相同的联通块长度最长。

数据

输入

第一行n,kn,kn,k
接着nnn行每行一个数,第iii行表示第iii头牛的"血统编号"

输出

rt。删掉kkk种牛后最长的相同血统联通块长度。

样例

输入
9 1
2
7
3
7
7
3
7
5
7
输出
4

解释

删去血统编号为3的所有牛,剩余是
2, 7, 7, 7, 7, 5, 7,其中有长度为4的相同血统联通块(第222个~第555个)

思路

我们会发现,如果一段区间里有k+1k+1k+1种牛,我们删去kkk种,剩下的就只有一种了。

这个题,1e51e51e5的数据,(伟大的班长lyz:显然二进制枚举珂以过!),肯定不能二进制枚举吧。。。想个线性的枚举办法。

我们现在强人锁♂男规定我们以rrr为区间右端点,并且必须选上a[r]a[r]a[r]作为剩下的那一种牛,然后找一个最远的左端点lll使得区间中有恰好kkk种牛。此时的答案就是区间中a[r]a[r]a[r]这种牛在[l,r][l,r][l,r]出现的次数,这个答案求一下maxmaxmax就好了。有没有漏考虑情况呢?好像没有。。。乍一想,好像就O(n2)O(n^2)O(n2)了,虽然比lyzlyzlyz的二进制枚举快0x3f3f3f3f0x3f3f3f3f0x3f3f3f3f倍,但是还是不能过。。。并且只能过一个点,和二进制枚举的分一样高。。。
lyz:Oh,yeah♂

我们来想想如何 搞♂死lyz 优化。我们会发现,随着rrr的不断右移,种类也就不会减少,如果l想要保持区间恰好还剩下k+1种牛,就不会向左移动\color{red}种类也就不会减少,如果l想要保持区间恰好还剩下k+1种牛,就不会向左\\移动lk+1。显然。这样就有了一个单调性,脑海中第一个浮现的便是活泼可爱美丽的尺取法了。

还有一个问题:如何统计每一个种类出现了多少次呢?暴力开数组。。。1e91e91e9珂存不下啊!

其实,我们只要给每个种类分配一个编号就珂以了。我们并不需要知道一个牛的血统编号是多少,只要把不同血统的牛编号不同,相同血统的牛血统编号相同即可。所以就按照输入顺序编上新编号,如果出现过就按照编好的号赋值,如果没有出现过就编上一个新的号。这个过程用STLSTLSTL的福利map&lt;&gt;map&lt;&gt;map<>实现。

STL,一时用一时爽,一直用一直爽

代码:

#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
    #define N 1001000
    map<int,int>mp;
    //重新编号
    int n,k,a[N];
    void Input()
    {
        int tmp=0;//当前用的编号
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&a[i]);
            if (!mp[a[i]]) mp[a[i]]=++tmp;//没有出现过就新编一个号
            a[i]=mp[a[i]];//使用新编号
        }
    }

    int cnt[N];
    //由于新编的号都<=n,所以用数组就珂以存下当前每个编号选了多少个
    void Solve()
    {
        int type=0;
        //选了多少编号
        int l=1,r=0;
        int ans=-1;
        while(l<=n and r<=n)
        {
            ++r;
            if (cnt[a[r]]==0) ++type;
            //如果是第一次出现,那么就相当于多选了一个类型,要++type
            ++cnt[a[r]];//这个一定是要加的,无疑问
            while(type>k+1)//必须要保证恰好选k+1个
            {
                --cnt[a[l]];//去除现在的l
                if (cnt[a[l]]==0) --type;//如果没减没了,type也要--
                ++l;//l右移
            }
            ans=max(ans,cnt[a[r]]);//由于我们要强♂制选a[r]这个种类,所以要用cnt[a[r]]更新答案
        }
        printf("%d\n",ans);
    }

    void Main()
    {
        Input();
        Solve();
    }
};
main()
{
    Flandle_Scarlet::Main();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值