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 \right |\)的值最小。

分析:

有这样一个结论:\(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:\n", 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\n", ans);
		}
		printf("\n");
	}

	return 0;
}
posted @ 2016-04-01 23:37  AOQNRMGYXLMV  阅读(184)  评论(0编辑  收藏  举报