HDU 3473 Minimum Sum (划分树)
题意:给定一个数组,有Q次的询问,每次询问的格式为(l,r),表示求区间中一个数x,使得sum = sigma|x - xi|最小(i在[l,r]之间),输出最小的sum。
思路:本题一定是要O(nlogn)或更低复杂度的算法。首先很容易得出这个x的值一定是区间(l,r)的中位数的取值,排序之后,也就是假设区间(l,r)长度为len ,则中位数就是该区间的第(r - l) / 2 - 1小的元素,求一个区间的第K小元素的算法很自然地会想到划分树, 而且划分树的查询复杂度为:O(logn),正好可以解决此题。
算法确定了之后就是具体的实现过程了,普通的划分树求的是区间内的第k小的元素,而这题是要求差值,也就是说我们不但要求出区间的第k小的元素,还要求出所有比中位数小的数 lsum,当然比中位数大的数的和可以根据区间的数的总和和lsum求得,因此不需要额外求。这样我们只需要在划分树建树的时候增加一个lsum[ ][i] 数组就可以了, 这个数组保存的是,在step层,在i前面被划分到左子树的元素之和。这样我们就可以求出最后的解了。
#include <iostream>
#include <algorithm>
#include <cmath>
#include<functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <climits>//形如INT_MAX一类的
#define MAX 100005
#define INF 0x7FFFFFFF
#define L(x) x << 1
#define R(x) x << 1 | 1
using namespace std; struct Seg_Tree {
int l,r,mid;
} tr[MAX*4];
int sorted[MAX];
int lef[20][MAX];
int val[20][MAX];
__int64 lsum[20][MAX];
__int64 sum[MAX];
__int64 summ; void build(int l,int r,int step,int x) {
tr[x].l = l;
tr[x].r = r;
tr[x].mid = (l + r) >> 1;
if(tr[x].l == tr[x].r) return ;
int mid = tr[x].mid;
int lsame = mid - l + 1;//lsame表示和val_mid相等且分到左边的
for(int i = l ; i <= r ; i ++) {
if(val[step][i] < sorted[mid]) {
lsame --;//先假设左边的数(mid - l + 1)个都等于val_mid,然后把实际上小于val_mid的减去
}
}
int lpos = l;
int rpos = mid + 1;
int same = 0;
for(int i = l ; i <= r ; i ++) {
if(i == l) {
lef[step][i] = 0;//lef[i]表示[ tr[x].l , i ]区域里有多少个数分到左边
lsum[step][i] = 0;
} else {
lef[step][i] = lef[step][i-1];
lsum[step][i] = lsum[step][i-1];
}
if(val[step][i] < sorted[mid]) {
lef[step][i] ++;
lsum[step][i] += val[step][i];
val[step + 1][lpos++] = val[step][i];
} else if(val[step][i] > sorted[mid]) {
val[step+1][rpos++] = val[step][i];
} else {
if(same < lsame) {//有lsame的数是分到左边的
same ++;
lef[step][i] ++;
lsum[step][i] += val[step][i];
val[step+1][lpos++] = val[step][i];
} else {
val[step+1][rpos++] = val[step][i];
}
}
}
build(l,mid,step+1,L(x));
build(mid+1,r,step+1,R(x));
} int query(int l,int r,int k,int step,int x) {
if(l == r) {
return val[step][l];
}
int s;//s表示[l , r]有多少个分到左边
int ss;//ss表示 [tr[x].l , l-1 ]有多少个分到左边
__int64 tmp = 0;
if(l == tr[x].l) {
tmp = lsum[step][r];
s = lef[step][r];
ss = 0;
} else {
tmp = lsum[step][r] - lsum[step][l-1];
s = lef[step][r] - lef[step][l-1];
ss = lef[step][l-1];
}
if(s >= k) {//有多于k个分到左边,显然去左儿子区间找第k个
int newl = tr[x].l + ss;
int newr = tr[x].l + ss + s - 1;//计算出新的映射区间
return query(newl,newr,k,step+1,L(x));
} else {
summ += tmp;
int mid = tr[x].mid;
int bb = l - tr[x].l - ss;//bb表示 [tr[x].l , l-1 ]有多少个分到右边
int b = r - l + 1 - s;//b表示 [l , r]有多少个分到右边
int newl = mid + bb + 1;
int newr = mid + bb + b;
return query(newl,newr,k-s,step+1,R(x));
}
} void solve(int l,int r) {
l ++; r ++;
summ = 0;
int k = (r - l) / 2 + 1;
__int64 mid = query(l,r,k,0,1);
__int64 ans = (k - 1) * mid - summ;
ans += sum[r] - sum[l-1] - summ - (r - l - k + 2) * mid;
printf("%I64d\n",ans);
} int n,m,l,r;
int main() {
int T;
cin >> T;
int ca = 1;
while(T--) {
scanf("%d",&n);
memset(sum,0,sizeof(sum));
for(int i=1; i<=n; i++) {
scanf("%d",&val[0][i]);
sorted[i] = val[0][i];
sum[i] = sum[i-1] + val[0][i];
}
sort(sorted+1,sorted+1+n);
build(1,n,0,1);
scanf("%d",&m);
printf("Case #%d:\n",ca++);
for(int i=0; i<m; i++) {
scanf("%d%d",&l,&r);
solve(l,r);
}
puts("");
}
}
HDU 3473 Minimum Sum (划分树)的更多相关文章
- HDU 3473 Minimum Sum 划分树,数据结构 难度:1
http://acm.hdu.edu.cn/showproblem.php?pid=3473 划分树模板题目,需要注意的是划分树的k是由1开始的 划分树: 参考:http://blog.csdn.ne ...
- HDU 3473 Minimum Sum 划分树
题意: 给出一个长度为\(n(1 \leq n \leq 10^5)\)的序列\(a\) 有若干次查询l r:找到一个\(x\)使得\(\sum \limits_{l \leq i \leq r} \ ...
- HDU 3473 Minimum Sum (划分树求区间第k大带求和)(转)
题意:在区间中找一个数,求出该区间每个数与这个数距离的总和,使其最小 找的数字是中位数(若是偶数个,则中间随便哪个都可)接着找到该区间比此数大的数的总和 区间中位数可以使用划分树,然后在其中记录:每层 ...
- HDU 3473 Minimum Sum(划分树)
Minimum Sum Time Limit: 16000/8000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tota ...
- hdu 3473 Minimum Sum
传送门 之前看挑战的时候看到一道分桶法的题目,其实我不是很明白分桶法应该怎么写.看到poj后面的讨论版上写着划分树裸题,而我以前就听说过了划分树,就干脆拿来学习一下.在写这篇博客的时候,其实我还是对这 ...
- HDU-3743 Minimum Sum,划分树模板
Minimum Sum 被这个题坑了一下午,原来只需找一个最中间的数即可,我以为是平均数. 题意:找一个数使得这个数和区间内所有数的差的绝对值最小.输出最小值. 开始用线段树来了一发果断T了,然后各种 ...
- hdu 3473 裸的划分树
思路: 用Sum[dep][i]记录从tree[po].l到i中进入左子树的和. #include<iostream> #include<algorithm> #include ...
- 【HDOJ】3473 Minimum Sum
划分树解.主席树解MLE. /* 3473 */ #include <iostream> #include <sstream> #include <string> ...
- hdu 2665 Kth number(划分树模板)
http://acm.hdu.edu.cn/showproblem.php?pid=2665 [ poj 2104 2761 ] 改变一下输入就可以过 http://poj.org/problem? ...
随机推荐
- 基于visual Studio2013解决C语言竞赛题之0902文件查找
题目
- 程序集的内部结构(托管模块、元素局、IL代码的分布情况)
程序集的内部结构 在看程序集的结构之前,我们先来看托管模块的结构. 托管模块由四部分组成:PE32头.CLR头.元数据(Metadata).IL代码.其中PE32头是用来决定托管模块运行的系统环境(3 ...
- 总线接口与计算机通信(三)UART起止式异步通用串行数据总线
串口简介 1. 什么是串口? 串口是计算机上一种非常通用的设备通信的协议.串口通信的概念非常简单,串口按位(bit) 发送和接收字节.尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送 ...
- Eclipse用法和技巧二十八:Eclipse插件Easy Explore的今世
先说明一下easyexplore插件的功能,easyexplore是一个类似于 Windows Explorer的Eclipse插件,它可以帮助你在不退出Eclipse的环境下迅速浏览本地文件系统. ...
- MongoDB---性能优化---(1)
MONGODB数据架构 升级解决.计划 发现问题 应用server用户数的突然涌入,创建server反应慢 检查server,我发现,每次反应非常慢,至30ops 检查过程 .发现数据库查询缓 ...
- HDU 3966 Aragorn's Story(树链剖分)
HDU Aragorn's Story 题目链接 树抛入门裸题,这题是区间改动单点查询,于是套树状数组就OK了 代码: #include <cstdio> #include <cst ...
- 【剑指offer】树的子结构
转载请注明出处:http://blog.csdn.net/ns_code/article/details/25907685 剑指offer第18题,九度OJ上測试通过! 题目描写叙述: 输入两颗二叉树 ...
- 极限挑战—C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码)
原文:极限挑战-C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码) 实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本实验将使用5中方法完成这个过程,并详细记录各种方 ...
- DM6446开发攻略——u-boot-1.3.4移植(1)
http://zjbintsystem.blog.51cto.com/964211/282387转载 UBOOT的版本更新速度比较快,截止今天,稳定正式的版本是u-boot-2009.11-rc2 ...
- uva 568(数学)
题解:从1開始乘到n,由于结果仅仅要最后一位.所以每乘完一次,仅仅要保留后5位(少了值会不准确,刚開始仅仅保留了一位.结果到15就错了,保留多了int会溢出,比方3125就会出错) 和下一个数相乘,接 ...