hdu 3473 Minimum Sum 划分树的应用

链接: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;
}

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

时间: 2024-11-05 22:35:06

hdu 3473 Minimum Sum 划分树的应用的相关文章

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

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

HDU 3473 Minimum Sum 划分树,数据结构 难度:1

http://acm.hdu.edu.cn/showproblem.php?pid=3473 划分树模板题目,需要注意的是划分树的k是由1开始的 划分树: 参考:http://blog.csdn.net/shiqi_614/article/details/8041390 划分树的定义 划分树定义为,它的每一个节点保存区间[lft,rht]所有元素,元素顺序与原数组(输入)相同,但是,两个子树的元素为该节点所有元素排序后(rht-lft+1)/2个进入左子树,其余的到右子树,同时维护一个num域,

HDU 3473 Minimum Sum 划分树

题目大意:给定一个序列,每次询问给出一个区间,我们需要选择一个数,这个数到区间内所有数的距离之和最小,求最小和 由绝对值不等式可得 当我们选择的这个数是中位数的时候距离和最小 于是这题就转换成了区间第k小 但是这题求的是最小和 于是我们做一个处理 我们多维护一个sum域 sum[i]表示[l,i]区间内划分到左子树中元素的总和 然后我们每次查询第k小时 如果我们进入的是右子树 就把划分到左子树中的元素和累加到left_sum上 然后用前缀和计算出区间的和 计算出right_sum 最后的结果就是

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

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

HDU 3473 Minimum Sum

Minimum Sum Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 2667    Accepted Submission(s): 609 Problem Description You are given N positive integers, denoted as x0, x1 ... xN-1. Then give you

HDU 1394 Minimum Inversion Number 树状数组&amp;&amp;线段树

题目给了你一串序列,然后每次 把最后一个数提到最前面来,直到原来的第一个数到了最后一个,每次操作都会产生一个新的序列,这个序列具有一个逆序数的值,问最小的你逆序数的值为多少 逆序数么 最好想到的是树状数组,敲了一把很快,注意把握把最后一个数提上来对逆序数的影响即可, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #i

HDU 4825 Xor Sum 字典树+位运算

点击打开链接 Xor Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others) Total Submission(s): 291    Accepted Submission(s): 151 Problem Description Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus

hdu 4828 Xor Sum (trie 树模板题,经典应用)

hdu 4825 题目链接 题意:给定n个数,然后给出m个询问,每组询问一个数x,问n中的数y使得x和y的异或和最大. 思路:字典树..把每个数转化成二进制,注意补全前导0,使得所有数都有相同的位数. 如果想要异或和最大,那么每一位尽可能都是1. 所以做法是,先构建字典树,然后每次find的时候,尽可能按照和当前寻找的数的位相反的位的方向走(如果有的话) 比如当前位是1,那我就往0的方向走. 需要注意的是,多组数据,每次要重新初始化一遍. 做法是 在struct 中重新 root = new N

HDU 3473-Minimum Sum(划分树-求区间sigma最小值)

Minimum Sum Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3710    Accepted Submission(s): 852 Problem Description You are given N positive integers, denoted as x0, x1 ... xN-1. Then give you