标签:划分树
链接:http://acm.hdu.edu.cn/showproblem.php?pid=3473
as small as possible!
. Output a blank line after every test case.2 5 3 6 2 2 4 2 1 4 0 2 2 7 7 2 0 1 1 1
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;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:划分树
原文地址:http://blog.csdn.net/u013532224/article/details/47271967