【HDU3473 Minimum Sum】划分树+中位数性质

本文介绍了一种使用划分树解决特定序列中中位数及其绝对差之和问题的方法。通过构建划分树并维护前缀和,可以在限定的空间内高效查询指定区间内的中位数,同时计算出该区间内所有数值与中位数绝对差之和。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意


给定一个序列,有q组询问,询问l~r中
在这里插入图片描述
最小的值。(x可以任选)

思路

首先,给定一个从小到大的数列x1,x2,……,xn,设x是从x1到xn与其绝对差之和最小的数,则显然x位于x1与xn之间。
那么,由于x1,xn与它们之间的任意一点的距离之和都相等,且都等于xn-x1,因此接下来可以不考虑x1与xn,而考虑剩下的从x2到x[n-1]的数,同样显然有x必然位于x2和x[n-1]之间。
依次类推,最后得出的结论是x就是该数列中间的那个数,或者是中间的那两个数之一,而这个数就是中位数。

结论:数列的中位数就是该数列各个数与其绝对差之和最小的数。
(引用自dalao博客
所以现在问题变成了如何求出上图中的值。
直接去绝对值就会发现,
a n s = 比 中 位 数 大 的 数 的 和 − 比 中 位 数 小 的 数 的 和 ans=比中位数大的数的和-比中位数小的数的和 ans=
由于空间限制,这道题不可以用主席树,只能用划分树啦。
在建树时,多维护一个sum,表示划分的该区间起始到下标 i 的数的前缀和。
查询时,可直接查询中位数,顺便求出ans。
查询时引入四个变量:
特别声明一下,L、R表示当前查询的区间左右端点,l、r表示询问的区间的左右端点!

    int l1 = toleft[dep][l-1] - toleft[dep][L-1];//L~l中前一半个数 
    int r1 = l-L-l1;// L~l中后一半个数 
    int l2 = cnt;// l~r中前一半个数 
    int r2 = r-l+1-cnt;//l~r中后一半个数 

这四个变量均表示个数,便于之后计算ans。
以当前查询的中位数在左子树中为例,有以下程序:

if(cnt >= k){
/*********************************************************************/
    	if(r2 > 0)
    		if(r1 > 0) ans += sum[dep+1][mid+r1+r2] - sum[dep+1][mid+r1];
    		else ans += sum[dep+1][mid + r2];
/********************************************************************/
        int newl=L+l1;
        int newr=newl+cnt-1;
        return query(L,mid,newl,newr,dep+1,k);
    }

比正常划分树新增部分,即/*********************/中间的部分,为维护ans(显然哈哈哈)。由于中位数在左子树中,右子树中的数比中位数大,所以ans应该加上这些数。如果r2>0,那么就表示有数比中位数大,并且在询问区间左右端点内,答案需要加上这部分数;而如果r1>0,就表示在查询区间左端点到询问区间左端点内也有数比中位数大的,答案需要减去这一部分数。
(可能我说的有点凌乱,其实自己画个图,小小琢磨一下,就懂啦)

代码

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=100010;
int tree[20][MAXN];
int sorted[MAXN];
long long sum[20][MAXN],ans;//存下本小节1~i的前缀和 
int toleft[20][MAXN];

void build(int l,int r,int dep){
 if(l == r){
 	sum[dep][l] = tree[dep][l];
 	return;
 }
 int mid = (l+r)>>1;
 int same = mid-l+1;
 for(int i = l; i <= r; ++ i){
 	if(tree[dep][i] < sorted[mid]) same --;
     sum[dep][i] = tree[dep][i];
     if(i != l) sum[dep][i] += sum[dep][i-1];
 }
   
 int lpos = l;
 int rpos = mid+1;
 for(int i = l; i <= r; ++ i){
     if(tree[dep][i] < sorted[mid]) 
 		tree[dep+1][lpos ++] = tree[dep][i];
     else if(tree[dep][i] == sorted[mid] && same > 0) 
 		tree[dep+1][lpos ++] = tree[dep][i], same --;
     else  
 		tree[dep+1][rpos ++] = tree[dep][i];
     toleft[dep][i] = toleft[dep][l-1]+lpos-l;
 }
 build(l,mid,dep+1);
 build(mid+1,r,dep+1);
}

int query(int L,int R,int l,int r,int dep,int k){
 if(l == r) return tree[dep][l];
 int mid = (L+R)>>1;
 int cnt = toleft[dep][r]-toleft[dep][l-1];
 int l1 = toleft[dep][l-1] - toleft[dep][L-1];//L~l中前一半个数 
 int r1 = l-L-l1;// L~l中后一半个数 
 int l2 = cnt;// l~r中前一半个数 
 int r2 = r-l+1-cnt;//l~r中后一半个数 
 //因为是绝对值,前减后加 
 if(cnt >= k){
 	if(r2 > 0){
 		if(r1 > 0) ans += sum[dep+1][mid+r1+r2] - sum[dep+1][mid+r1];//把后一半都加上 
 		else ans += sum[dep+1][mid + r2];
 	}
     int newl=L+l1;
     int newr=newl+cnt-1;
     return query(L,mid,newl,newr,dep+1,k);
 }
 else{
 	if(l2 > 0){
 		if(l1 > 0) ans -= sum[dep+1][L-1+l1+l2] - sum[dep+1][L-1+l1];
 		else ans -= sum[dep+1][L-1+l2];
 	}
      int newr=r+toleft[dep][R]-toleft[dep][r]; 
      int newl=newr-(r-l-cnt);
      return query(mid+1,R,newl,newr,dep+1,k-cnt);
 }
}


int main(){
 int n,m,ct=0;
 int s,t,k,Tcase;
 scanf("%d",&Tcase);
 while(Tcase --){
 	printf("Case #%d:\n",++ ct);
 	scanf("%d",&n);
     memset(tree,0,sizeof(tree)); //这个必须
     for(int i = 1; i <= n; ++ i){ //从1开始
         scanf("%d", &tree[0][i]);
         sorted[i] = tree[0][i];
     }
     sort(sorted+1, sorted+n+1);
     build(1,n,0);
     scanf("%d",&m);
     while(m --){
         scanf("%d%d",&s,&t);
         ++ s,++ t;
         ans = 0;
         long long mid = query(1,n,s,t,0,(t-s)/2+1);//取中位数 
         if((t-s+1)%2 == 0) ans -= mid;
         printf("%lld\n",ans);
     }
     printf("\n");
 }
 return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值