hdu 3473 Minimum Sum 再来一波划分树,对划分树累觉不爱。

原创 2015年02月07日 13:02:22

Minimum Sum

Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3084    Accepted Submission(s): 710


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
我感觉划分树在遍历的时候最痛苦。哎,总是在推区间,区间真的很容易推错。

有一个数列 x1..xn,要求一个数x使得 ∑(abs(xi-x))值最小,很明显,对数列进行排序后最中间的那个数就是x,可用划分树求得,那么如何求和呢,经过计算可知,既然

x 是最中间的那个数,那么最后的和 即为 x左边 xmid-x1+xmid-x2.. +  x(mid+1) - xmid + x(mid+2)-xmid..  整理得 xmid*(lefnum-rignum)+rigsum-lefsum

lefnum为划分过程进入左子树的个数,lefsum为进入左子树的数之和

上面是正常的思路,我来说一个非常规的思路,int ans = 0 ;我们直接遍历的时候,如果中位数为x,如果x在左子树,那我们ans+=区间内进入右子树所有数之和。如果x在右子树,那我们ans-=区间内进入左子树的,一直到找到x结束。这样我们就巧妙的上面的俩个过程合在了一起
哎,由于错把循环变量j写成了i,,拿着别人的代码调试了一晚上,结果代码和别人的长的差不多了,,无力
另外需要注意的是,,这道题对内存要求的比较紧,,数组不能太大。
下面是代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAX 100100

using namespace std ;

long long sum[25][MAX];
int sorted[MAX] , tree[25][MAX] ,toLeft[25][MAX] ;
void creat(int L , int R , int deep)
{
	if(L == R)
	{
		sum[deep][L]=tree[deep][L] ;
		return ;
	}
	int mid = (L+R)>>1 , same = mid-L+1;
	
	for(int i = L ; i <= R ; ++i)
	{
		if(tree[deep][i]<sorted[mid])
			--same ;
		sum[deep][i] = tree[deep][i] ;
		if(i>L)	sum[deep][i] += sum[deep][i-1] ;
	}
	int ls = L ,rs = mid+1;
	for(int i = L ; i <= R ; ++i)
	{
		int flag = 0 ;
		long long num = 0;
		if(tree[deep][i]<sorted[mid] || (tree[deep][i]==sorted[mid] && same))
		{
			tree[deep+1][ls++]=tree[deep][i] ;
			if(tree[deep][i] == sorted[mid])
				--same ;
			flag = 1 ;
		}
		else
		{
			tree[deep+1][rs++] = tree[deep][i] ; 
		}
		toLeft[deep][i] = toLeft[deep][i-1]+flag ;
	}
	creat(L,mid,deep+1) ;
	creat(mid+1,R,deep+1) ;
}
long long ans = 0;
int query(int L , int R , int x , int y , int k , int deep)
{
	if(x == y)
	{
		return tree[deep][x] ;
	}
	int  mid = (L+R)>>1;
	int lxl = toLeft[deep][x-1] - toLeft[deep][L-1] ;
	int lyl = toLeft[deep][y] - toLeft[deep][L-1] ;
	int xyl = toLeft[deep][y] - toLeft[deep][x-1] ;
	int lxr = x-L-lxl ;
	int lyr = y-L+1-(toLeft[deep][y]-toLeft[deep][L-1]) ;
	
	if(k<=xyl)	
	{
		if(lyr>0)
		{
			if(lxr>0)
			{
				ans += sum[deep+1][mid+lyr]-sum[deep+1][mid+lxr];
			}
			else
			{
				ans += sum[deep+1][mid+lyr] ;
			}
		}
		return query(L,mid,L+lxl,L+lyl-1,k,deep+1);
	}
	else	
	{
		if(lyl>0)
		{
			if(lxl>0)	ans-=sum[deep+1][L+lyl-1]-sum[deep+1][L+lxl-1];
			else	ans -= sum[deep+1][L+lyl-1] ;
		}
		return query(mid+1,R,mid+lxr+1,mid+lyr,k-xyl,deep+1);
	}
	
}

int main()
{
	int t ;
	scanf("%d",&t);
	for(int i = 1 ; i <= t ; ++i)
	{
		int n ;
		scanf("%d",&n);
		memset(toLeft,0,sizeof(toLeft)) ;
		for(int j = 1 ; j <= n ; ++j)
		{
			scanf("%d",&sorted[j]) ;
			tree[0][j] = sorted[j] ;
		}
		sort(sorted+1 , sorted+n+1) ;
		creat(1,n,0) ;
		int r ;
		printf("Case #%d:\n",i) ;
		scanf("%d",&r) ;
		for(int j = 0 ; j < r ; ++j)
		{
			int l,ri;
			scanf("%d%d",&l,&ri) ;
			++l,++ri;
			int k = (ri-l)/2+1;
			ans = 0 ;
			int d = query(1,n,l,ri,k,0);
			if((ri-l+1)%2 == 0)
			{
				ans -= d ;
			}
			printf("%I64d\n",ans);
		}
		printf("\n") ;
	}
	return 0 ;
}


相关文章推荐

【划分树】 HDU 3473 Minimum Sum 中位数

点击打开链接 题意:在给出有N个数的数列 然后Q个询问 询问  [ L,R ]    要那个公式的值最小 显然是这个区间的中位数嘛 然后就要处理出 小于中位数的数的和 大于中位数的数的和 对于划分树来...

hdu 3473 Minimum Sum 划分树的应用

题意: 问l,r区间 取任意整数x , 问 ∑|X-xi| 的最小值 做法:很明显 这个X是中位数。 中位数想到划分树,然后在划分树 建树的时候,把进左子树的数计算一个前缀和,然后在查询的时候,...

hdu 3473 Minimum Sum(划分树应用)

Minimum Sum Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) T...
  • fp_hzq
  • fp_hzq
  • 2012年09月19日 10:33
  • 621

hdu 3473 Minimum Sum 划分树

http://acm.hdu.edu.cn/showproblem.php?pid=3473 题意:给定一个数组,有Q次的询问,每次询问的格式为(l,r),表示寻找一个x,使得的值最小。N 思路:...

hdu 3473 Minimum Sum(划分树)

题意:给你n个数,m个查询,对于每个查询,问在区间[a,b]内找到一个数x,使得最小。 同样是划分树,只是多记录了一个sum域,表示进入左子树的数的和。 如果不懂划分树:http://blog.c...

HDU 3473 Minimum Sum 划分树

题目大意:给定一个序列,每次询问给出一个区间,我们需要选择一个数,这个数到区间内所有数的距离之和最小,求最小和 由绝对值不等式可得 当我们选择的这个数是中位数的时候距离和最小 于是这题就转换成了区间...
  • PoPoQQQ
  • PoPoQQQ
  • 2014年09月24日 15:12
  • 1134

hdu 3473 Minimum Sum(划分树-sum操作)

划分树。只是考虑求当前区间大于第k值的值得和,和小于第k值的和。显然可以在查询的时候直接搞出来。sum[d][i]表示第d层子区间l,r种l-i的和。写错了一个下标,检查了半辈子。。。 #includ...

hdu 3473(划分树)

划分树。 第二次写划分树,累的要死,回头发现第一次写的是错的……-_-||,看来poj2104的数据太弱了…… x取中位数ave,用sum[dep][i]保存划分的dep层前i个之和,查询时通过这...
  • wxy__
  • wxy__
  • 2011年10月03日 01:14
  • 539

hdu3473(划分树)

Minimum Sum Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) T...

HDU 3473 划分树

点击打开链接 题意:问你给定区间的给出的表达式的最小值 思路:看了网上题解,都说中位数便是最优的解,证明不会,那么跟着思路做就行了,用划分树找出中位数然后在划分树中加入区间的前缀和即可,加的时候只...
  • Dan__ge
  • Dan__ge
  • 2016年06月14日 21:28
  • 1380
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:hdu 3473 Minimum Sum 再来一波划分树,对划分树累觉不爱。
举报原因:
原因补充:

(最多只允许输入30个字)