HDU 3473 Minimum Sum(划分树)
题目大意
给你 n(1<=n<=10^5) 个数,分别是x(0), x(1), x(2), ..., x(n-1)。再给你 Q(1<=Q<=10^5) 个 query。
每个 query 要求对于编号在区间 [L, R] 中的所有数,找出一个 x,使得 segma{ abs( x(i)-x ) } (L<=i<=R)最小,输出这个最小的值
做法分析
首先可以肯定的是,对于一个给定的 query 区间 [st, en],我们需要找到的 x 肯定是 x(st), x(st+1), ..., x(en) 的中位数
找到中位数之后,接下来就是求解所有的距离和了
找中位数可以用 划分树 log(n) 的做。但是怎么求所有的距离和呢?
我们可以在找中位数的过程中,递归的求解这个和,而这也就是本题的难点!
比如当前树中的区间是 [L, R] ,而我们查找的区间是 [st, en],找的是其中的第 k 个(L<=st<=en<=R)。
假设已经找到了第 k 个是多大了,设为 keyVal
假设 keyVal 位于当前树节点的左儿子中,我们需要将所有在 [st, en] 中的,分配在了右儿子中的数全部减去 keyVal,再加到我们的 ans 中。
如果 keyVal 位与当期树节点的右儿子中,我们需要用 keyVal 减去每个在 [st, en] 中的,分配在了左儿子中的数,再加到我们的 ans 中
这里,我们不难想到用部分和来优化这个过程:
定义 sum[deep][i] 表示第 deep 层,从 0 到 i 的所有数的和
然后在执行上面的过程即可
参考代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 6 using namespace std; 7 8 const int N=100006; 9 typedef long long LL; 10 11 struct devided_tree 12 { 13 int val[20][N], sorted[N], f[20][N]; 14 LL sum[20][N], ans; 15 16 void build(int L, int R, int deep) 17 { 18 int mid=(L+R)>>1; 19 int midVal=sorted[mid], tot=mid-L+1; 20 for(int i=L; i<=R; i++) 21 { 22 if(val[deep][i]<midVal) tot--; 23 sum[deep][i]=(i==L)?val[deep][i]:val[deep][i]+sum[deep][i-1]; 24 } 25 if(L==R) return; 26 int sL=L, sR=mid+1; 27 for(int i=L; i<=R; i++) 28 { 29 f[deep][i]=(i==L)?0:f[deep][i-1]; 30 if(val[deep][i]<midVal || val[deep][i]==midVal && tot) 31 { 32 val[deep+1][sL++]=val[deep][i]; 33 if(val[deep][i]==midVal) tot--; 34 f[deep][i]++; 35 } 36 else val[deep+1][sR++]=val[deep][i]; 37 } 38 build(L, mid, deep+1), build(mid+1, R, deep+1); 39 } 40 41 int query(int L, int R, int st, int en, int k, int deep) 42 { 43 if(st==en) return val[deep][st]; 44 int mid=(L+R)>>1; 45 int L1=(L==st)?0:f[deep][st-1]; 46 int L2=f[deep][en]-L1; 47 int R1=(st-L)-L1; 48 int R2=(en-st+1)-L2; 49 if(k<=L2) 50 { 51 int keyVal=query(L, mid, L+L1, L+L1+L2-1, k, deep+1); 52 if(R2>0) 53 { 54 if(R1==0) ans=ans-keyVal*(LL)R2+sum[deep+1][mid+R1+R2]; 55 else ans=ans-sum[deep+1][mid+R1]-keyVal*(LL)R2+sum[deep+1][mid+R1+R2]; 56 } 57 return keyVal; 58 } 59 else 60 { 61 int keyVal=query(mid+1, R, mid+1+R1, mid+1+R1+R2-1, k-L2, deep+1); 62 if(L2>0) 63 { 64 if(L1==0) ans=ans-sum[deep+1][L+L1+L2-1]+keyVal*(LL)L2; 65 else ans=ans-sum[deep+1][L+L1+L2-1]+sum[deep+1][L+L1-1]+keyVal*(LL)L2; 66 } 67 return keyVal; 68 } 69 } 70 } tree; 71 72 int main() 73 { 74 int n, m, t; 75 scanf("%d", &t); 76 for(int ca=1; ca<=t; ca++) 77 { 78 printf("Case #%d:\n", ca); 79 scanf("%d", &n); 80 for(int i=0; i<n; i++) 81 { 82 scanf("%d", &tree.val[0][i]); 83 tree.sorted[i]=tree.val[0][i]; 84 } 85 sort(tree.sorted, tree.sorted+n); 86 tree.build(0, n-1, 0); 87 scanf("%d", &m); 88 for(int i=0, st, en; i<m; i++) 89 { 90 scanf("%d%d", &st, &en); 91 tree.ans=0; 92 tree.query(0, n-1, st, en, (en-st+2)/2, 0); 93 printf("%I64d\n", tree.ans); 94 } 95 printf("\n"); 96 } 97 return 0; 98 }
AC通道