Loading [MathJax]/jax/output/CommonHTML/jax.js

HDU 3473 Minimum Sum 划分树

题意:

给出一个长度为n(1n105)的序列a
有若干次查询l r:找到一个x使得lir|xai|的值最小。

分析:

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

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

对于这道题就可以很方便地计算出答案:计算出中位数的大小mid,中位数左右两侧数字的个数cntl,cntr以及的对应的和suml,sumr
最终答案就是:(midcntlsuml)+(sumrmidcntr)

#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 @   AOQNRMGYXLMV  阅读(184)  评论(0编辑  收藏  举报
编辑推荐:
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
阅读排行:
· 如何给本地部署的DeepSeek投喂数据,让他更懂你
· 超详细,DeepSeek 接入PyCharm实现AI编程!(支持本地部署DeepSeek及官方Dee
· 用 DeepSeek 给对象做个网站,她一定感动坏了
· .NET 8.0 + Linux 香橙派,实现高效的 IoT 数据采集与控制解决方案
· .NET中 泛型 + 依赖注入 的实现与应用
历史上的今天:
2015-04-01 UVa 11992 (线段树 区间修改) Fast Matrix Operations
点击右上角即可分享
微信分享提示