B. Range and Partition
题意:
给定一个长度为
n
n
n 的数组
a
a
a。你需要确定一个范围
[
x
,
y
]
[x,y]
[x,y],并将
a
a
a 数组分成
k
k
k 段,使得对于每一段,在范围
[
x
,
y
]
[x,y]
[x,y] 以内的元素个数大于在范围
[
x
,
y
]
[x,y]
[x,y] 以外的元素个数。请求出任意一组使得
y
−
x
y-x
y−x 最小的
x
,
y
x,y
x,y 和划分的方案。
思路:
题解
这类问题可以通过正负性解决
记区间内的为
+
1
+1
+1,区间外的为
−
1
-1
−1
如果一段区间
[
l
,
r
]
[l,r]
[l,r] 满足条件,则
p
r
e
[
r
]
−
p
r
e
[
l
−
1
]
>
=
1
pre[r]-pre[l-1] >= 1
pre[r]−pre[l−1]>=1
从头遍历找出
k
k
k 个满足条件的区间即可
问题是求出区间
[
x
,
y
]
[x,y]
[x,y],并最小化
设
S
=
∑
i
=
1
n
p
r
e
i
S=\sum_{i=1}^npre_i
S=∑i=1nprei,如果
S
>
=
k
S>=k
S>=k 则一定可行(从
1
1
1 到
k
k
k,每增加
1
1
1 就会出现一段满足条件的区间)
显然随着区间范围增大,满足条件的可能性是非递减的
枚举
x
x
x,二分
y
y
y,维护最小区间即得到
[
x
,
y
]
[x,y]
[x,y]
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e5 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
int a[maxn], b[maxn];
int pre[maxn];
int find(int x){
int l = x, r = n;
while(l < r){
int mid = l + r >> 1;
int d = pre[mid] - pre[x-1];
if(d - (n - d) >= m) r = mid;
else l = mid + 1;
}
if(2 * (pre[l] - pre[x-1]) - n >= m) return l;
else return -1;
}
void work()
{
cin >> n >> m;
for(int i = 0; i <= n; ++i) pre[i] = 0;
for(int i = 1; i <= n; ++i) {
cin >> a[i];pre[a[i]]++;
}
for(int i = 1; i <= n; ++i) pre[i] += pre[i-1];
int dl = 1, dr = n;
for(int i = 1; i <= n; ++i){
int r = find(i);
if(r != -1 && dr - dl + 1 > r - i + 1) dl = i, dr = r;
}
cout << dl << " " << dr << endl;
for(int i = 1; i <= n; ++i){
if(a[i] >= dl && a[i] <= dr) pre[i] = 1;
else pre[i] = -1;
}
for(int i = 1; i <= n; ++i) pre[i] += pre[i-1];
int l = 1, cnt = 0;
for(int i = 1; i <= n && cnt + 1 < m; ++i){
if(pre[i] - pre[l-1] > 0){
cout << l << " " << i << endl;
l = i + 1;
++cnt;
}
}
cout << l << " " << n << endl;
}
int main()
{
ios::sync_with_stdio(0);
int TT;cin>>TT;while(TT--)
work();
return 0;
}