时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB
样例输入
2 4 7 1 1 2 3 3 6 100 100 100
样例输出
0 3又是一题二分题,比赛时没做出来。。。。看别人代码懂了。。
思路:问第k小的结果,就二分答案。用双指针(好像专业点的方法叫尺取法)验证答案。
几个点要考虑:
1.k会爆int,答案也会爆int
2.尺取法的算法思路
3.二分别写错了。。。- -反正我经常写错
-
#include <bits/stdc++.h>
-
const
int N =
200010;
-
using
namespace
std;
-
typedef
long
long ll;
-
-
int num[N];
-
int a[N], n;
-
ll k;
-
map<
int,
int>mp;
-
bool judge(ll x)
-
{
-
ll cur =
0, rk =
0;
-
int l =
0;
-
for (
int i =
0; i < n; i++)
-
a[i] =
0;
//统计当前有多少个数
-
-
for (
int i =
0; i < n; i++)
-
{
-
cur += a[num[i]];
//把当前数贡献的价值加进去
-
a[num[i]]++;
-
while (cur > x)
//要求小于等于k的区间价值个数,值大于x要减去
-
{
-
cur -= --a[num[l++]];
//把最开始的数价值贡献减去
-
}
-
rk += i - l +
1;
//如果[l, i]区间的价值小于等于x,那么i不动,l增加,
-
//得到的价值只会更小,所以以l开头,i结尾的区间都符合要求
-
}
-
return rk >= k;
//小于等于当前值的区间数大于等于k,就把答案扩进二分的区间了。
-
}
-
int main()
-
{
-
int T;
-
scanf(
"%d", &T);
-
while (T--)
-
{
-
int tot =
0;
-
mp.clear();
-
scanf(
"%d%lld", &n, &k);
-
for (
int i =
0; i < n; i++)
-
{
-
scanf(
"%d", num + i);
-
if (mp.find(num[i]) == mp.end())
-
mp[num[i]] = tot++;
//离散化方便后面统计
-
num[i] = mp[num[i]];
-
}
-
ll l =
0, r = (ll)n*(n
-1)/
2;
-
while (l < r)
-
{
-
ll mid = (l + r)>>
1;
-
if (judge(mid))
-
r = mid;
//答案在区间里面,则更小范围的区间是[l, mid]
-
else
-
l = mid +
1;
//答案不在区间在答案在[mid+1, r]区间
-
}
-
printf(
"%lld\n", l);
-
}
-
return
0;
-
}