【划分树】HDU 3473
2012-02-16 14:24
302 查看
题意就是求[l,r]中位数与其余元素差的绝对值再求和
方法:用划分树求出中位数,询问的同时求出中位数左边元素和还有左边元素个数,因此这里需要增加一个数组suml[dep][i]表示深度为dep的一段区间里划分到左边的元素和,求出来后就可以算出右边元素和还有右边元素个数,最后的结果就是ans = rs-ls+mid*(lcnt-rcnt),具体看代码和注释。
注意有可能溢出,所以凡是跟元素和有关的变量都要long long!在这里wa了几次.....囧
方法:用划分树求出中位数,询问的同时求出中位数左边元素和还有左边元素个数,因此这里需要增加一个数组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; }
相关文章推荐
- hdu 3473 划分树求范围内小于中位数的和与大于中位数的和
- hdu 3473 划分树
- 【划分树】 HDU 3473 Minimum Sum 中位数
- HDU 3473 Minimum Sum 划分树
- hdu 3473 划分树
- hdu 3473 划分树
- hdu 3473 Minimum Sum 划分树
- HDU 3473 划分树求某段区间的和
- hdu 3473 划分树 ***
- Minimum Sum - HDU 3473 划分树
- HDU 3473 Minimum Sum【划分树】
- hdu 3473 Minimum Sum 划分树的应用
- hdu 3473 Minimum Sum 划分树
- HDU 3473 划分树
- hdu 3473 Minimum Sum(划分树-sum操作)
- hdu 3473 裸的划分树
- HDU 3473 Minimum Sum 划分树
- HDU 3473 Minimum Sum (划分树求区间第k大带求和)(转)
- hdu1028 Ignatius and the Princess III(DP整数划分)
- hdu 3473 Minimum Sum