牛客寒假训练 温澈滢的狗狗 二分 + 计数

题目在这

考虑暴力做法:从小到大枚举亲密度,算出各个亲密度下有多少对数,然后相加直到为k。时间复杂度为O(n ^ 2)

二分做法:二分亲密度mid,算出亲密度<=mid时有多少对,如果有大于等于k对时答案显然在左边,否则在右边。

然后问题就是怎么算出亲密度<=mid有多少对数。比赛时就死在这里,想到了可能是二分亲密度,但没有想到怎么算有多少对数。这里给出两种方法。
①直接法:因为要求亲密度<=mid的对数,因此,可以枚举长度为mid 的区间。使用双指针算法,左指针为l,右指针为r,每次右指针向右移动一位,算出他左边长度为mid的区间内有多少和它不一样的数,然后再左指针右移。过程中直接用桶记录数据即可。
②考虑容斥:长度为mid的区间的满足要求的对数 = 所有对数-不满足条件的对数(即相同的数)(1)可以按照如上方法进行双指针的算法。
(2)也可以开一个vector数组,将所有相同的数放在一个桶,然后分别对每个桶枚举即可。

#include <iostream>
#include <cstring>

const int N = 1e5 + 10;
using namespace std;
typedef long long LL;
LL n; LL k;
int a[N], st[N];

LL check(LL mid)
{
    memset(st, 0, sizeof st);
    LL sum = 0;
    for (int i = 1; i <= mid + 1 && i <= n; i ++)
    {
        sum += ((i - 1) - st[a[i]]);
        st[a[i]] ++;
    }
    LL l = 1, r = mid + 2;
    while(r <= n)
    {
        st[a[l]] --;
        l ++;
        sum += (mid - st[a[r]]);
        st[a[r]] ++;
        r ++;
    }
    // cout << "---" << mid << "--" << sum << endl;
    return sum;
}

int main()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i ++)   cin >> a[i];
    
    LL l = 1, r = n + 1;
    while(l < r)
    {
        LL mid = l + r >> 1;
        if (check(mid) >= k) r = mid;
        else l = mid + 1;
    }
    LL cnt = check(l - 1);
    cnt = k - cnt;
    LL num = 0;
    
    if (l == n + 1)
        cout << -1;
    else
    {
        for(int i = 1; ;i ++)
        {
            if (a[i] != a[i + l])
                num ++;
            if (num == cnt)
            {
                cout << i << ' ' << i + l;
                break;
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值