您的位置:首页 > 其它

【划分树】HDU 3473

2012-02-16 14:24 302 查看
题意就是求[l,r]中位数与其余元素差的绝对值再求和


方法:用划分树求出中位数,询问的同时求出中位数左边元素和还有左边元素个数,因此这里需要增加一个数组suml[dep][i]表示深度为dep的一段区间里划分到左边的元素和,求出来后就可以算出右边元素和还有右边元素个数,最后的结果就是ans = rs-ls+mid*(lcnt-rcnt),具体看代码和注释。

注意有可能溢出,所以凡是跟元素和有关的变量都要long long!在这里wa了几次.....囧

#define N 100005
int a
, as
;//原数组,排序后数组
int sum[20]
;//记录第i层的1~j划分到左子树的元素个数(包括j)
int tree[20]
;//记录第i层元素序列
LL suml[20]
;//凡是跟suml有关的变量都要long long!
int n, m;
void build(int c, int l, int r){
int i, mid = (l + r) >> 1, lm = mid - l + 1, lp = l, rp = mid + 1;
for (i = l; i <= mid; i++){
if (as[i] < as[mid]){
lm--;//先假设左边的(mid - l + 1)个数都等于as[mid],然后把实际上小于as[mid]的减去
}
}
for (i = l; i <= r; i++){
if (i == l){
sum[c][i] = 0;//sum[i]表示[l, i]内有多少个数分到左边,用DP来维护
suml[c][i] = 0;
}else{
sum[c][i] = sum[c][i-1];
suml[c][i] = suml[c][i-1];
}
if (tree[c][i] == as[mid]){
if (lm){
lm--;
sum[c][i]++;
suml[c][i] += tree[c][i];
tree[c + 1][lp++] = tree[c][i];
}else
tree[c + 1][rp++] = tree[c][i];
} else if (tree[c][i] < as[mid]){
sum[c][i]++;
suml[c][i] += tree[c][i];
tree[c + 1][lp++] = tree[c][i];
} else{
tree[c + 1][rp++] = tree[c][i];
}
}
if (l == r)return;
build(c + 1, l, mid);
build(c + 1, mid + 1, r);
}
LL ls,rs;//中位数左边和,右边和
int lcnt,rcnt;//中位数左边个数,右边个数
int query(int c, int l, int r, int ql, int qr, int k){
int s;
int ss;
LL x,xx;
int mid = (l + r) >> 1;
if (l == r){
return tree[c][l];
}
if (l == ql){
s = 0;
ss = sum[c][qr];
x = 0;
xx = suml[c][qr];
}else{
s = sum[c][ql - 1];
ss = sum[c][qr] - s;
x = suml[c][ql-1];
xx = suml[c][qr] - x;
}
if (k <= ss){
return query(c + 1, l, mid, l + s, l + s + ss - 1, k);
}else{//划分到左边的都比中位数小
ls += xx;
lcnt += ss;
return query(c + 1, mid + 1, r, mid - l + 1 + ql - s, mid - l + 1 + qr - s - ss,k - ss);
}
}
LL lin
;//前i个的和
int main(){
int i, j, k;
int ca;
scanf("%d",&ca);
int tt = 1;
while(ca--){
int n;
scanf("%d",&n);
lin[0] = 0;
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
as[i] = tree[0][i] = a[i];
lin[i] = lin[i-1] + a[i];
}
sort(as+1,as+1+n);
build(0,1,n);
scanf("%d",&m);
printf("Case #%d:\n",tt++);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
x++,y++;
k = (y-x)/2+1;
ls = rs = 0;
lcnt = rcnt = 0;
int mid = query(0,1,n,x,y,k);
rs = lin[y]-lin[x-1]-ls-mid;//右边和
rcnt = (y-x)-lcnt;//右边个数
if(x == y)rs = ls = 0,lcnt = rcnt = 0;
LL ans = rs-ls+mid*(lcnt-rcnt);
printf("%I64d\n",ans);
}
printf("\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: