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 的所有数的和

然后在执行上面的过程即可

 

参考代码

 

HDU 3473
 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通道

 

HDU 3473 Minimum Sum

 

 

 

posted @ 2013-03-14 11:48  jianzhang.zj  阅读(427)  评论(0编辑  收藏  举报