TIOJ-1208

給你一個數列S,一個該數列的連續和(Continuous Sum,以下簡稱CS)是指S當中的某些連續項之總和。
很容易算得出來,一個總長度為項的數列S,其連續和(CS)共有$\frac{n(n+1)}{2}$個。 注意,問題來囉!

請問,這$\frac{n(n+1)}{2}$個連續和(CS)之中,第k大的是多少?

$n\leq 20000, |a_i|\leq 10000$


二分搜+merge sort
複雜度 $O(nlognlogC), C=20000\times 10000\times 2$

結果卻狂吃TLE 花了3個小時debug 丟了27筆submission
問了幾個電神 其中一個叫我注意二分搜
我原本範圍設
l=-200000025, r=200000025
改成沒負數之後
他過了
他過了
他過了
我好可撥

後來才想到 當$l=-1,\ r=0$時 $r=m$會無法更新 以後隨時提醒自己二分搜有負數時要特別小心@@

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <bits/stdc++.h>
using namespace std;

vector<int> tmp;

int num(int ans, int l, int r) {
if(l>=r) return 0;

int m = (l+r)/2;
int cnt = num(ans, l, m)+num(ans, m+1, r);
int it = l;

for(int i = m+1; i <= r; i++) {
while(it<=m && tmp[i]-tmp[it]>ans) {
it++;
}
cnt+=(m+1)-it;
}

vector<int> www;
int a=l, b=m+1;
for(int i = 0; i < r-l+1; i++) {
if(a>m || (b<=r &&tmp[b]<tmp[a])) {
www.push_back(tmp[b]);
b++;
}
else {
www.push_back(tmp[a]);
a++;
}
}
for(int i = 0; i < r-l+1; i++) tmp[l+i]=www[i];

return cnt;
}

signed main() {
ios::sync_with_stdio(0); cin.tie(NULL);

int n, k;
while(cin >> n >> k) {
if(!n) break;
vector<int> sum(n+1);
for(int i = 1; i <= n; i++) {
cin >> sum[i];
sum[i]+=sum[i-1];
}
int l = 0, r=400000050;
int rk = ((n+1)*n)/2-k+1;

while(l<r) {
tmp=sum;
int m = (l+r)/2;
int cnt = num(m-200000025, 0, n);
if(cnt < rk) l=m+1;
else r=m;
}
cout << l-200000025 << '\n';
}

}