每日一题——Genius ACM
题目
题解
我们这题使用倍增+归并的思想,要使平方差最大当然是离越远越好,那么自然而然会想到排序。
-
检查合法性函数
check(l, r):- 提取子数组 ( a[l:r] ) 并拷贝到数组
b进行排序。 - 计算前 ( m ) 对最大平方差和。
- 如果总和小于等于 ( k ),返回
true,表示该子段可行。
- 提取子数组 ( a[l:r] ) 并拷贝到数组
-
倍增搜索找最优分割点:
- 采用倍增(指数增长)查找当前段能扩展到的最大右边界:
- 设
p = 1(步长),r = l(当前右边界)。 - 若
check(l, r + p)可行,则扩展r += p,并倍增p *= 2。 - 否则
p /= 2,进行细粒度搜索。
- 设
- 找到最大合法段后,计数
cnt++,更新l = r,继续下一段查找。
- 采用倍增(指数增长)查找当前段能扩展到的最大右边界:
-
复杂度分析
check(l, r): 需要对b排序,复杂度为 ( O(nlog n) )。- 每次
while(l < n):- 指数倍增查找 ( O(log n) ) 次
check(l, r)。 - 整体最坏情况 ( O(n(logn)^2) )。
- 指数倍增查找 ( O(log n) ) 次
我们提交答案发现代码运行居然超时了,那我们该怎么进行优化呢。我们会发现对于排序片段中,有部分是上次就排序好了,我们不需要将他们再进行一次排序,只需要把后来加入的排序,最后归并一下就好了。此时优化后的代码时间复杂度就来到了O(nlogn)。
参考代码(优化前)
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int N = 5e5 + 10;
int a[N],b[N];
int n, m, T;
bool check(int l, int r){
int k = 0;
for(int i = l; i < r; i ++) b[k ++] = a[i];
sort(b, b + k);
int sum = 0;
for(int i = 0; i < m && i < k; i ++ , k --) sum += (b[k - 1] - b[i]) * (b[k - 1] - b[i]);
return sum <= T;
}
void solve(){
cin >> n >> m >> T;
for(int i = 0; i < n; i ++) cin >> a[i];
int l = 0, cnt = 0;
while(l < n){
int p = 1, r = l;
while(p){
if(r + p <= n && check(l, r + p)){
r += p;
p *= 2;
}else{
p /= 2;
}
}
cnt ++;
l = r;
}
cout << cnt << endl;
}
signed main(){
int _ = 1;
cin >> _;
while(_ --){
solve();
}
return 0;
}
参考代码(优化后)
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int N = 5e5 + 10;
int a[N],b[N],t[N];
int n, m, T;
bool check(int l, int mid, int r){
for(int i = mid; i < r; i ++) b[i] = a[i];
sort(b + mid, b + r);
int i = l, j = mid, k = 0;
while(i < mid && j < r){
if(b[i] < b[j]) t[k ++] = b[i ++];
else t[k ++] = b[j ++];
}
while(i < mid) t[k ++] = b[i ++];
while(j < r) t[k ++] = b[j ++];
int sum = 0;
for(int i = 0; i < m && i < k; i ++ , k --) sum += (t[k - 1] - t[i]) * (t[k - 1] - t[i]);
return sum <= T;
}
void solve(){
cin >> n >> m >> T;
for(int i = 0; i < n; i ++) cin >> a[i];
int l = 0, cnt = 0;
while(l < n){
int p = 1, r = l;
while(p){
if(r + p <= n && check(l, r, r + p)){
r += p;
p *= 2;
for(int i = l; i < r; i ++) b[i] = t[i - l];
}else{
p /= 2;
}
}
cnt ++;
l = r;
}
cout << cnt << endl;
}
signed main(){
int _ = 1;
cin >> _;
while(_ --){
solve();
}
return 0;
}

浙公网安备 33010602011771号