首先看到最小化 y − x y-x y−x 这个玩意,果断二分 y − x y-x y−x,显然假如区间更长合法,那么更短一定也合法,然后再枚举 x x x 进而得到 [ x , y ] [x,y] [x,y],接下来的事情就是要判断某一个区间 [ x , y ] [x,y] [x,y] 是否合法。
我们首先把砍成 k k k 段转化为尽可能地多段,因为如果 t t t 段的分割是合法的,我们我们将最后两个区间合并,这就是 t − 1 t-1 t−1 段了。
我们将 a i a_i ai 重新赋值,如果 a i ∈ [ x , y ] a_i \in [x,y] ai∈[x,y] 那么赋值为 1,否则为 -1,这样我们分割的 k k k 段区间每一段的 a i a_i ai 的和满足是正数即可,鉴于这是区间求和,搞个前缀和 s i = ∑ j = 1 i a j s_i=\sum\limits_{j=1}^ia_j si=j=1∑iaj,条件变成了 s r − s l − 1 > 0 , s r > s l − 1 s_r-s_{l-1}\gt0,s_r>s_{l-1} sr−sl−1>0,sr>sl−1, l − 1 l-1 l−1 为刚好上一个区间的右端点,也就是说我们在求一个 s s s 的上升子序列并且长度为 k + 1 k+1 k+1,起点为 s 0 = 0 s_0=0 s0=0,终点为 s n s_n sn。于是搞完这一切发现复杂度还是不行。
但是我们发现其实还可以抢救一下,我们发现 s s s 有个奇妙的性质,就是 ∣ s i − s i − 1 ∣ = 1 |s_i-s_{i-1}|=1 ∣si−si−1∣=1,所以如果 s s s 里有 x > 0 x>0 x>0,那么 s s s 一定还有 0 , 1 , 2 ⋯ x − 2 , x − 1 0,1,2\cdots x-2,x-1 0,1,2⋯x−2,x−1,并且下标存在在其之前的。这个最长上升子序列是可以构造出来的,只需要中间是连续的正整数。还有最后一个条件那就是序列长度要有 k + 1 k+1 k+1,这很简单只要让 s n ≥ k s_n\ge k sn≥k 就行。所以我们判定区间合不合法只需要计算一个 s n s_n sn 就完了。
回顾 s n s_n sn 定义,我们令 t = ∑ i = 1 n [ a i ∈ [ x , y ] ] t=\sum\limits_{i=1}^n[a_i \in [x,y]] t=i=1∑n[ai∈[x,y]],显然 s n = t − ( n − t ) = 2 t − n s_n=t-(n-t)=2t-n sn=t−(n−t)=2t−n,也就是在 [ x , y ] [x,y] [x,y] 的数的个数减去不在的。我们再用一个前缀和便于查询在某个值域区间的数的个数来计算 s n s_n sn,于是这题就结束了,吗?
我当时以为到这就没了,结果发现题目还让我去求方案,人傻了。这其实也简单,既然都知道了 [ x , y ] [x,y] [x,y],我们可以按照上升子序列的求解方式搞,也可以直接贪心,因为保证了 [ x , y ] [x,y] [x,y] 合法,所以我们区间的和为正的时候就划分一下,显然这是对的。
枚举 [ x , y ] [x,y] [x,y] 貌似也可以用双指针,但是我不会。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define cmin(a, b) (a)=min((a), (b))
#define cmax(a, b) (a)=max((a), (b))
#define mem(a, x) memset(a, x, sizeof(a))
#define rps(i, b, e) for(int i=(b);i<=(e);i++)
#define prs(i, e, b) for(int i=(e);i>=(b);i--)
#define rpg(i, g, x) for(int i=g.head[x];i;i=g.e[i].nxt)
#define opf(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
using namespace std;
template<class T>
inline void read(T &x) {
x=0;
bool f=false;
char c=getchar();
while(!isdigit(c))f|=(c=='-'), c=getchar();
while(isdigit(c))x=x*10+(c-'0'), c=getchar();
if(f)x=-x;
}
const int NR=2e5+10;
int T, n, k, a[NR], s[NR];
int chk(int dif) {
rps(l, 1, n-dif+1)
if((s[l+dif]-s[l-1])*2-n>=k)
return l;
return 0;
}
struct node {
int id, v, ct;
}st[NR], p[NR];
int top;
int main() {
read(T);
while(T--) {
read(n), read(k);
mem(s, 0);
rps(i, 1, n)read(a[i]), s[a[i]]++;
rps(i, 1, n)s[i]+=s[i-1];
int l=0, r=n-1, mid, yx=1e9, xx=1e9;
while(l<=r) {
mid=l+r>>1;
int res=chk(mid);
if(res==0)l=mid+1;
else yx=mid, xx=res, r=mid-1;
}
printf("%d %d\n", xx, xx+yx);
int yy=xx+yx;
l=1;
int subct=0, ns=0;
rps(i, 1, n) {
if(a[i]>=xx && a[i]<=yy)ns++;
else ns--;
if(ns<=0 || subct+1>=k)continue;
printf("%d %d\n", l, i);
l=i+1, subct++, ns=0;
}
printf("%d %d\n", l, n);
}
return 0;
}