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

154 篇文章 1 订阅
2 篇文章 0 订阅

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 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值