码迷,mamicode.com
首页 > 其他好文 > 详细

hdu 3473 Minimum Sum 划分树的应用

时间:2015-08-04 11:27:18      阅读:129      评论:0      收藏:0      [点我收藏+]

标签:划分树

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3473


Minimum Sum

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


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
 


题意:

问l,r区间  取任意整数x ,  问 ∑|X-xi|  的最小值

做法:很明显 这个X是中位数。 中位数想到划分树,然后在划分树 建树的时候,把进左子树的数计算一个前缀和,然后在查询的时候,如果到右子树,就把区间内进入左子树的数算一个和。最后统计出来的sumlft 就是所有比区间内中位数小的数了。


//O(log(n)) 找第k大的值
#include <iostream> 
#include <cstdio>
#include <algorithm>
using namespace std;

#define N 100009
#define M 19
//M>log2,N;
#define MID ((l+r)>>1)
#define ll __int64
int s[N];//s存的是 sort后的原始数据;
int t[M][N];//t存的是树;
int numb[M][N];//和t数组同步,当前层,当前区间,N下标前有多少个数划归到左子树.
int n,m; 
ll quzuo[M][N];
ll sum[N]; 
void Build(int c,int l,int r) //c是层数
{
	int lm=MID-l+1,lp=l,rp=MID+1; //lp左边的起点  rp 右边的起点
	for(int i=l;i<=MID;i++)
		lm-=s[i]<s[MID]; //lm 代表 现在的左块(下标l-MID)中 有多少等于s[MID]的 包括其本身

	int zuo,you;
	for(int i=l;i<=r;i++)
	{
		zuo=you=0;
		if( i==l )
		{
			numb[c][i]=0; 
			quzuo[c][i]=0;
		}
		else
		{
			numb[c][i]=numb[c][i-1];
			quzuo[c][i]=quzuo[c][i-1];
		}
		if(t[c][i]==s[MID])
		{
			if( lm )//左边有多少等于中位数的的 都归到左块去
			{
				lm--;
				zuo=1;
			}
			else//如果 等于左块的中位数用完了, 就放到右块去 
				you=1; 
		}
		else if( t[c][i]<s[MID] )//小的去左块 
			zuo=1;  
		else 
			you=1; 

		if(zuo)
		{  
			numb[c][i]++;
			t[c+1][lp++]=t[c][i];
			quzuo[c][i]+=t[c][i]; 
		}
		else
		{  
			t[c+1][rp++]=t[c][i];
		}
	}
	if( l<r )
		Build(c+1,l,MID),Build(c+1,MID+1,r);
}


__int64 sumlft,sumrit;
int numlft,numrit;
int Query(int c,int l,int r,int ql,int qr,int k)//ql和qr是查询的区间左右边界,  l和r 当前区间.
{
	if( l==r )
		return t[c][l];
	int s,ss;
	if( l==ql )
		s=0,ss=numb[c][qr];
	else
		s=numb[c][ql-1],ss=numb[c][qr]-numb[c][ql-1];

	if( k<=ss )
	{  
		return Query(c+1,l,MID,l+s,l+s+ss-1,k);
	}
	else
	{
		if(ql==l)
		{ 
			sumlft+=quzuo[c][qr]-0;
		}
		else
		{ 
			sumlft+=quzuo[c][qr]-quzuo[c][ql-1];
		}
		return Query(c+1,MID+1,r,MID+1+ql-l-s,MID+1+qr-l-s-ss,k-ss);// ss到左子树 所以ss在右子树里排名下降ss
	}
} 
int main()
{

	int tt;
	int cas=1;
	scanf("%d",&tt);
	while(tt--)
	{
		scanf("%d",&n);
		sum[0]=0;
		for(int i=1;i<=n;i++)
		{ 
			s[i]=i;
			scanf("%d",&s[i]);
			t[0][i]=s[i];
			sum[i]=sum[i-1]+(ll)s[i];
		}
		sort(s+1,s+1+n);
		Build(0,1,n);
		scanf("%d",&m);

		printf("Case #%d:\n",cas++);
		while( m-- )
		{
			int l,r;
			sumlft=sumrit=numlft=numrit=0;//在进入右子树的时候,把区间内 到左子树的值加到sumlft中
			scanf("%d%d",&l,&r);// l-r 中 第k大,l从1开始 c=0开始,
			l++;
			r++;
			int ans;
			ans=Query(0,1,n,l,r,(r-l)/2+1);
			numlft=(r-l)/2;
			numrit=r-l-numlft;
			sumrit=sum[r]-sum[l-1]-sumlft-(ll)ans; 

			//  printf("zuohe %d youhe %d  zuoshu %d youshu %d ans%d \n",sumlft,sumrit,numlft,numrit,ans);
			printf("%I64d\n",((ll)numlft*(ll)ans-sumlft)+(sumrit-(ll)numrit*(ll)ans));
		}
		puts("");
	}
	return 0;
}



版权声明:本文为博主原创文章,未经博主允许不得转载。

hdu 3473 Minimum Sum 划分树的应用

标签:划分树

原文地址:http://blog.csdn.net/u013532224/article/details/47271967

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!