划分树解这种求区间第k大,或者在加些变化的问题,真有效,577MS,限时8s。但不看板子,还敲不出来。
Minimum Sum
Time Limit: 16000/8000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4344 Accepted Submission(s): 981
Problem Description
You are given N positive integers, denoted as x0, x1 ... xN-1. Then give you some intervals [l, r]. For each interval, you need to find a number x to make
as small as possible!
Input
The first line is an integer T (T <= 10), indicating the number of test cases. For each test case, an integer N (1 <= N <= 100,000) comes first. Then comes N positive integers x (1 <= x <= 1,000, 000,000) in the next line. Finally, comes an integer Q (1 <= Q <= 100,000), indicting there are Q queries. Each query consists of two integers l, r (0 <= l <= r < N), meaning the interval you should deal with.
Output
For the k-th test case, first output “Case #k:” in a separate line. Then output Q lines, each line is the minimum value of
. Output a blank line after every test case.
Sample Input
2 5 3 6 2 2 4 2 1 4 0 2 2 7 7 2 0 1 1 1
Sample Output
Case #1: 6 4 Case #2: 0 0
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define N 100007
int splay[20][N], sorted[N];
int toLeft[20][N];//第i层第[1:j]之间有多少个数据放到了左边
long long leftSum[20][N], lsum, preSum[N];
int ln;
void build(int lv, int x, int y) {
if (x == y) return;
int mid = x + y >> 1;
int suppose = mid - x + 1; //假设中位数sorted[mid]左边的数全部小于sorted[mid]
for (int i = x; i <= y; ++i) {
if (splay[lv][i] < sorted[mid])
suppose--;
}
//如果suppose == 1, 则数组中值为sorted[mid]只有一个数
int lpos = x, rpos = mid + 1;
for (int i = x; i <= y; ++i) {
if (i == x) { //预处理,相当于初始化
toLeft[lv][i] = 0; //lv层[1:i]之间有多少个数据放到了左边
leftSum[lv][i] = 0;
}
else {
toLeft[lv][i] = toLeft[lv][i - 1];
leftSum[lv][i] = leftSum[lv][i - 1];
}
if (splay[lv][i] < sorted[mid]) { //划分到中位数的左边
toLeft[lv][i]++;
leftSum[lv][i] += splay[lv][i];
splay[lv + 1][lpos++] = splay[lv][i];
}
else if (splay[lv][i] > sorted[mid]) { //划分到中位数右边
splay[lv + 1][rpos++] = splay[lv][i];
}
else { //这里suppose大于0的数划分到左边
if (suppose != 0) {
suppose--;
toLeft[lv][i]++;
leftSum[lv][i] += splay[lv][i];
splay[lv + 1][lpos++] = splay[lv][i];
}
else {
splay[lv + 1][rpos++] = splay[lv][i];
}
}
}
build(lv + 1, x, mid);
build(lv + 1, mid + 1, y);
}
long long query(int lv, int l, int r, int L, int R, int k) {
//在[L, R]数据中查询第k大の数据
if (l == r) return splay[lv][l];
int s;//代表[L, l)之间有所少个元素被分到左边
int ss;//[L, R]内将被分到左子树的元素数目
int mid = l + r >> 1;
long long tmp = 0;
if (L == l) {
s = 0;
ss = toLeft[lv][R];
tmp = leftSum[lv][R];
}
else {
s = toLeft[lv][L - 1];
ss = toLeft[lv][R] - s;
tmp = leftSum[lv][R] - leftSum[lv][L - 1];
}
int newl, newr;
if (k <= ss) { //查询左边
newl = l + s;
newr = l + toLeft[lv][R] - 1; // s + ss == toLeft[lv]R]
return query(lv + 1, l, mid, newl, newr, k);
}
else { //查询右边
ln += ss;
lsum += tmp;
newl = mid - l + 1 + L - s;
newr = mid - l + 1 + R - toLeft[lv][R];
return query(lv + 1, mid + 1, r, newl, newr, k - ss);
}
}
int main()
{
int T, kase = 0;
scanf("%d", &T);
while (T-- > 0) {
preSum[0] = 0;
printf("Case #%d:\n", ++kase);
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &splay[0][i]);
sorted[i] = splay[0][i];
preSum[i] = preSum[i - 1] + splay[0][i];
}
std::sort(sorted + 1, sorted + 1 + n);
build(0, 1, n);
int q;
scanf("%d", &q);
while (q-- > 0) {
ln = 0;
lsum = 0;
int x, y;
scanf("%d%d", &x, &y);
x++, y++;
int k = y - x + 2 >> 1;
int midV = query(0, 1, n, x, y, k);
int rn = y - x + 1 - ln;
long long rsum = preSum[y] - preSum[x - 1] - lsum;
long long ans = rsum - lsum + midV * (ln - rn);
printf("%I64d\n", ans);
}
puts("");
}
return 0;
}