题目 题意:给定长度为

n

n

n的数组

a

a

a和

k

k

k,其中

1

<

=

a

i

<

=

n

1<=a_i<=n

1<=ai​<=n现在要求找出区间长度最小的

x

,

y

x,y

x,y,将数组

a

a

a划分成k个子数组,其中

a

i

a_i

ai​位于这k个子数组的其中之一。要求每个子数组中,位于

[

x

,

y

]

[x,y]

[x,y]范围内的元素,多于不在

[

x

,

y

]

[x,y]

[x,y]范围内的元素。 现在构造出x,y,以及这k个子数组的区间。 官方题解 思路:考虑x,y已知的情况。对于每个

a

i

a_i

ai​,如果

x

<

=

a

i

<

=

y

x<=a_i<=y

x<=ai​<=y,则+1,否则记为-1。那么要求每个子数组累计得分要为正。因此,我们初始得分为0,当得分首次大于0时,则截取当前区间,并开始取新的区间,得分重新归零。注意最后一个区间(即第k个子数组)保留。这里用的是前缀和的思想。

如何确定x,y。不妨设上述得分数组为

b

b

b,前缀和数组为

p

r

e

pre

pre。我们可以发现

b

b

b取值为1,-1;从而

p

r

e

pre

pre相邻元素差值绝对值为1,即

p

r

e

i

1

p

r

e

i

=

1

|pre_{i-1}-pre_i|=1

∣prei−1​−prei​∣=1。要使按照上述贪心思路,能找到k组子数组,说明

p

r

e

n

pre_n

pren​至少要有k,即

p

r

e

n

>

=

k

pre_n>=k

pren​>=k;此外,由于

p

r

e

pre

pre是连续变化的,我们必然可以找到

p

r

e

pre

pre取值为1,2,…,k的下标,我们取它们出现的第一次位置即可。

要使

p

r

e

n

>

=

k

pre_n>=k

pren​>=k,则有

2

i

=

1

n

[

x

<

=

a

i

<

=

y

]

n

>

=

k

2*\sum_{i=1}^{n}[x<=a_i<=y] - n>=k

2∗∑i=1n​[x<=ai​<=y]−n>=k,有

i

=

1

n

[

x

<

=

a

i

<

=

y

]

>

=

n

+

k

2

\sum_{i=1}^{n}[x<=a_i<=y] >=\lceil \frac{n+k}{2}\rceil

∑i=1n​[x<=ai​<=y]>=⌈2n+k​⌉ 我们可以对数组排序,枚举x从小到大,查看对应有

n

+

k

2

\lceil \frac{n+k}{2}\rceil

⌈2n+k​⌉数量的y的最小取值,所有的情况中,取

y

x

y-x

y−x最小的。

官方代码,写的真好

#include

using namespace std;

int main() {

int tc;

cin >> tc;

while( tc-- ){

int n, k;

cin >> n >> k;

vector a(n), sorted_a(n);

for(int i=0; i

cin >> a[i];

sorted_a[i] = a[i];

}

sort(sorted_a.begin(),sorted_a.end());

int req_sum = ( n + k + 1 ) / 2;

pair> ans = { n + 1 , { -1 , -1 } };

for(int i=0; i+req_sum-1

ans = min( ans , { sorted_a[i+req_sum-1] - sorted_a[i] , { sorted_a[i] , sorted_a[i+req_sum-1] } } );

cout << ans.second.first << ' ' << ans.second.second << '\n';

int subarrays_found = 0, curr_sum = 0;

int last_uncovered = 1;

for(int i=0; i

if( a[i] >= ans.second.first && a[i] <= ans.second.second ) curr_sum ++;

else curr_sum --;

if( curr_sum > 0 && subarrays_found + 1 < k ){

cout << last_uncovered << ' ' << ( i + 1 ) << '\n';

last_uncovered = i + 2;

subarrays_found ++;

curr_sum = 0;

}

}

subarrays_found ++;

cout << last_uncovered << ' ' << n << '\n';

}

return 0;

}

查看原文