http://acm.hdu.edu.cn/submit.php?pid=3473
HDU 3473 Minimum Sum
大意:已知n个正整数x0,x1,x2...xn-1排成一列,要求回答若干问题,
问题类型如下:
给你一个区间[l,r],要求sum(x-xi)(l<=i<=r)的最小值,其中x必须为xl,xl+1...xr中的一个数
分析:易推得对于任意区间[l,r],符合条件的x为所在区间的中位数,求任意区间的中位数可以用划分树
来解决,同样的,我们用suml[deep][i]来记录划分树中第deep层到数i位置放入左子树的数字的和,
注意:当最后得出中位数median左边的和suml及中位数右边的和sumr,以及它们所对应的个数lnum,rnum,
此时ans = sumr - suml +(lnum-rnum)*ave,切不可左右分开求,否者数据可能超过__int64,我就死在这里了,纠结了整整一下午。。~~~~(>_<)~~~~
View Code
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN = 100000+10;
typedef __int64 LL;
long tree[22][MAXN],toleft[22][MAXN];
LL lsum[22][MAXN];//lsum[deep][i]为第deep层到第xi被划入左子树的数字的和
LL sum[MAXN];//sum[i]为1。。i的和
long sorted[MAXN];//已经排好序的数据
LL suml;
int lnum;
void build_tree(int left,int right,int deep)//建树
{
if(left==right)return;
int mid = (left+right)>>1;
int i;
int same = mid-left+1;//位于左子树的数据
for(i=left;i<=right;i++) //计算放于左子树中与中位数相等的数字个数
{
if(tree[deep][i]<sorted[mid])
same--;
}
int ls = left;
int rs = mid+1;
for(i=left;i<=right;i++)
{
int flag = 0;
if((tree[deep][i]<sorted[mid])||(tree[deep][i]==sorted[mid]&&same>0))//放入左子树
{
flag = 1;
tree[deep+1][ls++] = tree[deep][i];
if(tree[deep][i]==sorted[mid])same--;
lsum[deep][i]=lsum[deep][i-1]+tree[deep][i];
}
else//右子树
{
tree[deep+1][rs++] = tree[deep][i];
lsum[deep][i]=lsum[deep][i-1];
}
toleft[deep][i]=toleft[deep][i-1]+flag;
}
build_tree(left,mid,deep+1);
build_tree(mid+1,right,deep+1);
}
int query(int left,int right,int k,int L,int R,int deep)
{
if(left==right)return tree[deep][left];
int mid = (L+R)>>1;
int x = toleft[deep][left-1]-toleft[deep][L-1];//位于left左边的放于左子树中的数字个数
int y = toleft[deep][right] - toleft[deep][L-1];//到right为止位于左子树的个数
int ry = right-L-y;//到right右边为止位于右子树的数字个数
int cnt = y-x;//[left,right]区间内放到左子树中的个数
int rx = left-L-x;//left左边放在右子树中的数字个数
if(cnt>=k)
return query(L+x,L+y-1,k,L,mid,deep+1);
else
{
lnum=lnum+cnt;
suml=suml+lsum[deep][right]-lsum[deep][left-1];
return query(mid+rx+1,mid+1+ry,k-cnt,mid+1,R,deep+1);
}
}
int main()
{
// freopen("E:/MYACM/Match/UESTC-4/Minimum Sum/1.in","r",stdin);
// freopen("2.out","w",stdout);
int n,m;
int T;
int cases = 0;
while(scanf("%d",&T)!=EOF)
{
cases = 0;
while(T--)
{
cases++;
scanf("%d",&n);
int i;
for(i=0;i<20;i++)
lsum[i][0]=0;
sum[0]=0;
for(i=1;i<=n;i++)
{
scanf("%d",&sorted[i]);
tree[0][i] = sorted[i];
sum[i]=sum[i-1]+sorted[i];
}
sort(sorted+1,sorted+n+1);
build_tree(1,n,0);
scanf("%d",&m);
printf("Case #%d:\n",cases);
while(m--)
{
int left,right,k;
scanf("%d%d",&left,&right);
left++;
right++;
k=((right-left)/2)+1;
suml = 0;
lnum = 0;
long ave = query(left,right,k,1,n,0);//求中位数
int rnum = (right-left+1-lnum);
LL sumr = sum[right]-sum[left-1]-suml;//右子树的和
LL ans = sumr-ave*(rnum-lnum) - suml;
printf("%I64d\n",ans);
}
printf("\n");
}
}
return 0;
}