区间价值
描述
给定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;
}