题目
题解思路
因为亲密度是第一键值,所以所有亲密关系,可以根据亲密度的情况来分类,并且每个亲密度下的关系有序(编号从小到大)。
即存在一个亲密度,它的亲密关系 + 之前更小的亲密度的关系 刚好 大于等于 K。
这里就有点分界点的意思了,所以我们尝试二分亲密度,看看能不能在On级别处理出这个亲密度以及一下的所有关系总数。
枚举出P为此时的亲密度。
这里双指针也太顶了,我们可以先处理出两个指针之间大小为P的区间的数的情况,即用哈希表统计每个数的情况。再用一个cnt来记录这个区间内和首指针不同的数的个数。
首指针往后的时候将这个数的影响删了。统计减1。
如何将首指针区间的成果转移到下一个数?
用前一个cnt减去下一个数的个数,加上前一个数出现的个数就是区间暂时的成果。
有了这个写check函数之间二分,然后找到值枚举出那个位置即可。
不要小瞧双指针。
AC代码
#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
int n ;
long long k ;
int a[200100] ;
bool che(int p )
{
long long sum = 0 ;
unordered_map <int , int > mp ;
long long cnt = 0 ;
for (int i = 1 , j = 1 ; i < n ; i++ )
{
while (j <= n && j - i <= p )
{
mp[a[j]]++;
if (a[j] != a[i])
cnt++;
j++;
}
//cout << cnt << " " << mp[a[i]] << " " << mp[a[i+1]] << "\n" ;
sum += cnt;
mp[a[i]]--;
cnt -= mp[a[i+1]] ;
cnt += mp[a[i]] ;
}
//cout << sum << "\n" ;
return sum >= k ;
}
long long dd (int p )
{
long long sum = 0 ;
unordered_map <int , int > mp ;
long long cnt = 0 ;
for (int i = 1 , j = 1 ; i < n ; i++ )
{
while (j <= n && j - i <= p )
{
mp[a[j]]++;
if (a[j] != a[i])
cnt++;
j++;
}
//cout << cnt << " " << mp[a[i]] << " " << mp[a[i+1]] << "\n" ;
sum += cnt;
mp[a[i]]--;
cnt -= mp[a[i+1]] ;
cnt += mp[a[i]] ;
}
return sum ;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> k ;
for (int i = 1 ; i <= n ; i++ )
cin >> a[i] ;
int t1 = 1 , t2 = n - 1 ;
while ( t1 < t2 )
{
int mid = t1 + t2 >> 1 ;
if ( che(mid) )
{
t2 = mid ;
}else
t1 = mid + 1 ;
}
long long sk = dd(t2);
if ( sk >= k )
{
long long st = 0 ;
sk = k - dd(t2-1) ;
int l = 1 , r = 1 + t2 ;
while ( r <= n )
{
if ( a[l] != a[r] )
{
st++;
if (st == sk )
{
cout << l << " " << r << "\n" ;
break ;
}
}
l++;
r++;
}
}else
cout << "-1\n" ;
return 0 ;
}