hdu 3473 划分树 ***

题目大意:有一个数列 x1..xn,要求一个数x使得 sigma(abs(xi-x))值最小,很明显,对数列进行排序后最中间的那个数就是x,可用划分树求得,那么如何求和呢,经过计算可知,既然

x 是最中间的那个数,那么最后的和 即为 x左边 xmid-x1+xmid-x2.. +  x(mid+1) - xmid + x(mid+2)-xmid..  整理得 xmid*(lefnum-rignum)+rigsum-lefsum

lefnum为划分过程进入左子树的个数,lefsum为进入左子树的数之和

lefsum求法:在划分过程,当该层 有数进入左子树即 加上 该数,具体见代码。。

需要理解划分树

链接:点我

  1 #include <stdio.h>
  2 #include <algorithm>
  3 #include <iostream>
  4 #include <string.h>
  5 using namespace std;
  6 const int MAXN = 100010;
  7 int tree[20][MAXN];
  8 int sorted[MAXN];
  9 int toleft[20][MAXN];
 10 long long sum[20][MAXN];
 11
 12 void build(int l,int r,int dep)
 13 {
 14     if(l == r)
 15     {
 16         sum[dep][l] = sum[dep][l-1]+tree[dep][l];
 17         return;
 18     }
 19     int mid = (l+r)>>1;
 20     int same = mid - l + 1;
 21     for(int i = l;i <= r;i++)
 22     {
 23         if(tree[dep][i] < sorted[mid])
 24             same--;
 25         sum[dep][i] += sum[dep][i-1]+tree[dep][i];
 26     }
 27     int lpos = l;
 28     int rpos = mid+1;
 29     for(int i = l;i <= r;i++)
 30     {
 31         if(tree[dep][i] < sorted[mid])
 32             tree[dep+1][lpos++] = tree[dep][i];
 33         else if(tree[dep][i] == sorted[mid] && same > 0)
 34         {
 35             tree[dep+1][lpos++] = tree[dep][i];
 36             same--;
 37         }
 38         else
 39             tree[dep+1][rpos++] = tree[dep][i];
 40         toleft[dep][i] = toleft[dep][l-1] + lpos - l;
 41     }
 42     build(l,mid,dep+1);
 43     build(mid+1,r,dep+1);
 44 }
 45 long long ans;
 46 int query(int L,int R,int l,int r,int dep,int k)
 47 {
 48     if(l == r)return tree[dep][l];
 49     int mid = (L+R)>>1;
 50     int cnt = toleft[dep][r] - toleft[dep][l-1];
 51     if(cnt >= k)
 52     {
 53         int ee = r-L+1-(toleft[dep][r]-toleft[dep][L-1])+mid;
 54         int ss = l-L-(toleft[dep][l-1]-toleft[dep][L-1])+mid;
 55
 56         ans += sum[dep+1][ee]-sum[dep+1][ss];
 57
 58         int newl = L + toleft[dep][l-1]-toleft[dep][L-1];
 59         int newr = newl + cnt -1;
 60         return query(L,mid,newl,newr,dep+1,k);
 61     }
 62     else
 63     {
 64         int s = L + toleft[dep][l-1] - toleft[dep][L-1];
 65         int e = s + cnt - 1;
 66
 67         ans -= sum[dep+1][e] - sum[dep+1][s-1];
 68
 69         int newr = r + toleft[dep][R] - toleft[dep][r];
 70         int newl = newr - (r-l+1-cnt) + 1;
 71         return query(mid+1,R,newl,newr,dep+1,k-cnt);
 72     }
 73 }
 74 int main()
 75 {
 76     int T;
 77     int n;
 78     scanf("%d",&T);
 79     int iCase = 0;
 80     while(T--)
 81     {
 82         iCase++;
 83         scanf("%d",&n);
 84         memset(tree,0,sizeof(tree));
 85         memset(sum,0,sizeof(sum));
 86         for(int i = 1;i <= n;i++)
 87         {
 88             scanf("%d",&tree[0][i]);
 89             sorted[i] = tree[0][i];
 90         }
 91         sort(sorted+1,sorted+n+1);
 92         build(1,n,0);
 93         printf("Case #%d:\n",iCase);
 94         int m,l,r;
 95         scanf("%d",&m);
 96         while(m--)
 97         {
 98             scanf("%d%d",&l,&r);
 99             l++;r++;
100             ans = 0;
101             int tmp = query(1,n,l,r,0,(l+r)/2-l+1);
102             if((l+r)%2)ans-=tmp;
103             printf("%I64d\n",ans);
104         }
105         printf("\n");
106     }
107     return 0;
108 }

2015/7/4

时间: 2024-12-31 05:23:42

hdu 3473 划分树 ***的相关文章

HDU 4417 划分树+二分

题意:有n个数,m个询问(l,r,k),问在区间[l,r] 有多少个数小于等于k. 划分树--查找区间第k大的数.... 利用划分树的性质,二分查找在区间[l,r]小于等于k的个数. 如果在区间第 i 大的数tmp>k,则往下找,如果tmp<k,往上找. #include<cstdio> #include<stdlib.h> #include<string.h> #include<string> #include<map> #incl

HDU 4417 (划分树+区间小于k统计)

题目链接:  http://acm.hdu.edu.cn/showproblem.php?pid=4417 题目大意:给定一个区间,以及一个k值,求该区间内小于等于k值的数的个数.注意区间是从0开始的. 解题思路: 首先这题线段树可以解.方法是维护一个区间最大值max,一个区间点个数s,如果k>max,则ans=s+Q(rson),否则ans=Q(lson). 然后也可以用求区间第K大的划分树来解决,在对原来求第K大的基础上改改就行,方法如下: Build方法同第K大. 对于Query: ①左区

hdu 2665 划分树

#include <cstdio> #include <iostream> #include <algorithm> #include <queue> #include <cmath> #include <cstring> #include <stack> #include <set> #include <map> #include <vector> using namespace st

hdu 2665 划分树模板题(可作为模板)

Kth number Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 6951    Accepted Submission(s): 2214 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The fi

HDU 3473 Minimum Sum 划分树,数据结构 难度:1

http://acm.hdu.edu.cn/showproblem.php?pid=3473 划分树模板题目,需要注意的是划分树的k是由1开始的 划分树: 参考:http://blog.csdn.net/shiqi_614/article/details/8041390 划分树的定义 划分树定义为,它的每一个节点保存区间[lft,rht]所有元素,元素顺序与原数组(输入)相同,但是,两个子树的元素为该节点所有元素排序后(rht-lft+1)/2个进入左子树,其余的到右子树,同时维护一个num域,

hdu 3473 Minimum Sum(划分树-sum操作)

划分树.只是考虑求当前区间大于第k值的值得和,和小于第k值的和.显然可以在查询的时候直接搞出来.sum[d][i]表示第d层子区间l,r种l-i的和.写错了一个下标,检查了半辈子... #include<algorithm> #include<iostream> #include<cstring> #include<vector> #include<cstdio> #include<cmath> #include<queue&g

hdu 3473 Minimum Sum 划分树的应用

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3473 Minimum Sum Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3427    Accepted Submission(s): 787 Problem Description You are given N positive i

hdu 3473 Minimum Sum 再来一波划分树,对划分树累觉不爱。

Minimum Sum Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3084    Accepted Submission(s): 710 Problem Description You are given N positive integers, denoted as x0, x1 ... xN-1. Then give you

hdu 2665 Kth number(划分树)

Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4602 Accepted Submission(s): 1468 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first l