• 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

  • 相关阅读:
    liunx 学习
    Tracert 命令使用说明图解
    好的程序员应该收集一些不错的 类和方法
    apache 多端口
    数组中随机抽取一个或多个单元 (0086一随机显示列表)
    PHP 应具备的知识 学习
    rdlc报表中不显示0
    教程:VS2010 之TFS入门指南
    ORA00161: 事务处理的分支长度 xx 非法 (允许的最大长度为 64) 解决方法
    DataGridView编辑
  • 原文地址:https://www.cnblogs.com/zhj5chengfeng/p/2959072.html
Copyright © 2020-2023  润新知