hiho区间价值

区间价值

描述

给定n个数A1…An,小Ho想了解AL…AR中有多少对元素值相同。小Ho把这个数目定义为区间[L,R]的价值,用v[L,R]表示。
例如1 1 1 2 2这五个数所组成的区间的价值为4。
现在小Ho想知道在所有的的v[L,R](1 <= L <= R <= n)中,第k小的值是多少。

输入

第一行一个数T(T<=10),表示数据组数。
对于每一组数据:
第一行两个数n,k(1<=n<=200,000,1<=k<=n*(n+1)/2)
第二行n个数A1…An(1<=Ai<=1,000,000,000)

输出

一个数表示答案。

思路

使用尺取法计算大于x价值和小于x价值的区间数量 二分法调整使小于x价值的区间数量正好小于k
则k就是要求的答案

代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX 200010
int arr[MAX], num[MAX], barr[MAX];

int insertSort(int array[], int low, int high)
{
        int i = low;
        for( i = low; i < high; i++)
        {
                int min = array[i], mini = i;
                int j = low + 1;
                for( j = i + 1; j <= high; j++)
                {
                        if(min > array[j]) min = array[j], mini = j;
                }

                if(i != mini)
                {
                        array[mini] = array[i];
                        array[i] = min;
                }
        }

        return 0;
}

// 排序 从小到大
int quickSort3(int array[], int low, int high)
{
        int left = low, right = high;
        if(left >= right - 10)
        {
                insertSort(array, left, right);
                return 0;
        }

        int mid = (left + right) / 2;

        if(array[left] > array[right])
        {
                // left > right
                int temp = array[left];
                array[left] = array[right];
                array[right] = temp;
        }

        if(array[mid] > array[right])
        {
                // right > mid
                int temp = array[mid];
                array[mid] = array[right];
                array[right] = temp;
        }

        if(array[left] < array[mid])
        {
                // mid < left
                int temp = array[left];
                array[left] = array[mid];
                array[mid] = temp;
        }

        int prot = array[low];

        while(left < right)
        {
                while(left < right && array[right] >= prot) right--;
                array[left] = array[right];

                while(left < right && array[left] < prot) left++;
                array[right] = array[left];
        }

        array[left] = prot;

        int i = low;
        for( i = high; i > right; i--)
        {
                if(array[i] == prot)
                {
                        right++;
                        array[i] = array[right];
                        array[right] = prot;
                }
        }

        quickSort3(array, low, left - 1);
        quickSort3(array, right + 1, high);

        return 0;
}

int find2(int tmp[], int len, int v)
{
        int l = 0, h = len, m;
        while(l < h)
        {
                m = (l + h) / 2;
                if(tmp[m] == v) return m;
                if(tmp[m] > v)
                {
                        h = m;
                }
                else
                {
                        l = m + 1;
                }
        }

        return -1;
}
/* ----------------------------------------------------------------------------*/
/**
 * @brief 离散化 
 * a为原数组 b为离散后的数组 len为数组实际长度
 * 注意 len 下标不可访问
 *
 * @param a[]
 * @param b[]
 * @param len
 *
 * @return 
 */
/* ----------------------------------------------------------------------------*/
int suoxiao(int a[], int b[], int len)
{
        int *tmp = (int *)malloc(sizeof(int) * len + 1);
        int i,j;

        for( i = 0; i < len; i++)
        {
                tmp[i] = a[i];
        }

        quickSort3(tmp, 0, len - 1);

        for( i = 1, j = 1; i < len; i++)
        {
                if( tmp[i] != tmp[i-1] && i > j)
                {
                        tmp[j] = tmp[i];
                        j++;
                }
        }

        // b数组使用转换后的数字
        for( i = 0; i < len; i++)
        {
                b[i] = find2(tmp, j, a[i]);
        }
        return 0;
}

/* ----------------------------------------------------------------------------*/
/**
 * @brief 使用尺取法计算 区间价值 < m 的区间数量
 *
 * 返回 区间数量 - k 的值 
 *
 * @param arr[]
 * @param n
 * @param m
 * @param k
 *
 * @return 
 */
/* ----------------------------------------------------------------------------*/
int chiqu(int arr[], int n, long long m, long long k)
{
        long long sum = 0; // sum 为价值 < m 的区间数量  
        int i, j;

        if(m == 0) return -1;
        long long value = 0;
        memset(num , 0, sizeof(num));

        num[arr[0]]++;
        for( i = 0, j = 0; i < n; i++)
        {
                while(j < n - 1 && value + num[arr[j+1]] < m)
                {
                        j++;
                        value += num[arr[j]];
                        num[arr[j]]++;
                }

#ifdef DEBUG
                printf("j = %d, i = %d, sum = %d\n", j, i, sum);
#endif
                sum += j - i + 1;

                // value
                num[arr[i]]--;
                value -= num[arr[i]];
        }

#ifdef DEBUG
        printf("sum = %d, k = %d\n", sum, k);
#endif

        if( sum >= k ) return 1;
        return -1;
}

long long chiqu_m(int arr[], int n, int k)
{
        long long l = 0, h = n * (n + 1 ) / 2, m;
        while(l < h)
        {
                m = (l + h + 1) / 2;
                int v = chiqu(arr, n, m, k);

#ifdef DEBUG
                printf("l = %d, h = %d, m = %d, v = %d\n", l, h, m, v);
#endif

                if( v > 0 ) h = m - 1;
                else l = m;
        }

        return l;
}

int main()
{
        int t, n;
        long long k;

        scanf("%d", &t);
        while(t--)
        {
                scanf("%d %lld", &n, &k);
                int i;
                for( i = 0; i < n; i++)
                {
                        scanf("%d", &arr[i]);
                }

                suoxiao(arr, barr, n);

                long long res = chiqu_m(barr, n, k);
                printf("%lld\n", res);
        }

        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值