hdu3473 划分树练习-CSDN博客

48 阅读2分钟

划分树解这种求区间第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;
}


\

\