HDU 3473 Minimum Sum 划分树

题意:

给出一个长度为(n(1 leq n leq 10^5))的序列(a)
有若干次查询l r:找到一个(x)使得(sum limits_{l leq i leq r} left | x-a_i ight |)的值最小。

分析:

有这样一个结论:(x)为子序列的中位数时差的绝对值之和最小。
证明也很简单:

将序列中的每个元素对应到数轴上的点,(x)是数轴上一个动点。
(x)左边有(l)个点,右边有(r)个点。
如果动点向右移动(Delta x)距离(而且保证移动后左右两侧点数不变),那么目标值就会变化(l Delta x - r Delta x)
如果(l<r),这个值会变小;如果(l>r),那么向左移动这个值会变小。
直到左右两侧点数相等。

对于这道题就可以很方便地计算出答案:计算出中位数的大小(mid),中位数左右两侧数字的个数(cnt_l,cnt_r)以及的对应的和(sum_l,sum_r)
最终答案就是:((mid cdot cnt_l - sum_l) + (sum_r - mid cdot cnt_r))

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
const int maxn = 100000 + 10;
const int maxd = 20;

int n;
int sorted[maxn];

int T[maxd][maxn], cnt[maxd][maxn];
LL sum[maxd][maxn], pre[maxn];

void build(int d, int L, int R) {
	int M = (L + R) / 2;
	int lsame = M - L + 1;
	for(int i = L; i <= R; i++)
		if(T[d][i] < sorted[M]) lsame--;
	int lpos = L, rpos = M + 1;
	for(int i = L; i <= R; i++) {
		if(i == L) { sum[d][i] = 0; cnt[d][i] = 0; }
		else { sum[d][i] = sum[d][i-1]; cnt[d][i] = cnt[d][i-1]; }
		if(T[d][i] < sorted[M] || (T[d][i] == sorted[M] && lsame)) {
			cnt[d][i]++;
			sum[d][i] += T[d][i];
			T[d+1][lpos++] = T[d][i];
			if(T[d][i] == sorted[M]) lsame--;
		} else T[d+1][rpos++] = T[d][i];
	}

	if(L < M) build(d + 1, L, M);
	if(M + 1 < R) build(d + 1, M + 1, R);
}

LL q_kth, q_sum;

void query(int d, int L, int R, int qL, int qR, int k) {
	if(L == R) { q_kth = T[d][L]; q_sum += T[d][L]; return; }
	int M = (L + R) / 2;
	int numl;
	if(qL == L) numl = 0;
	else numl = cnt[d][qL - 1];
	int numr = cnt[d][qR];
	int num = numr - numl;
	if(num >= k) {
		query(d + 1, L, M, L + numl, L + numr - 1, k);
	} else {
		LL suml;
		if(qL == L) suml = 0;
		else suml = sum[d][qL - 1];
		q_sum += sum[d][qR] - suml;
		numl = qL - L - numl;
		numr = qR - L + 1 - numr;
		query(d + 1, M+1, R, M+1+numl, M+numr, k - num);
	}
}

int main()
{
	int _; scanf("%d", &_);
	for(int kase = 1; kase <= _; kase++) {
		scanf("%d", &n);
		for(int i = 1; i <= n; i++) {
			scanf("%d", sorted + i);
			pre[i] = pre[i - 1] + sorted[i];
			T[0][i] = sorted[i];
		}
		sort(sorted + 1, sorted + 1 + n);
		build(0, 1, n);

		printf("Case #%d:
", kase);
		int q; scanf("%d", &q);
		while(q--) {
			int l, r; scanf("%d%d", &l, &r);
			l++; r++;
			int k = (r - l) / 2 + 1;
			q_sum = 0;
			query(0, 1, n, l, r, k);
			LL ans = q_kth * k - q_sum;
			ans += (pre[r] - pre[l-1] - q_sum) - q_kth * (r - l + 1 - k);
			printf("%lld
", ans);
		}
		printf("
");
	}

	return 0;
}
原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/5346676.html