zoukankan      html  css  js  c++  java
  • HDU 3473 Minimum Sum【划分树】


    http://acm.hdu.edu.cn/submit.php?pid=3473
    HDU 3473 Minimum Sum
    大意:已知n个正整数x0,x1,x2...xn-1排成一列,要求回答若干问题,
    问题类型如下:
    给你一个区间[l,r],要求sum(x-xi)(l<=i<=r)的最小值,其中x必须为xl,xl+1...xr中的一个数

    分析:易推得对于任意区间[l,r],符合条件的x为所在区间的中位数,求任意区间的中位数可以用划分树
    来解决,同样的,我们用suml[deep][i]来记录划分树中第deep层到数i位置放入左子树的数字的和,
    注意:当最后得出中位数median左边的和suml及中位数右边的和sumr,以及它们所对应的个数lnum,rnum,
    此时ans = sumr - suml +(lnum-rnum)*ave,切不可左右分开求,否者数据可能超过__int64,我就死在这里了,纠结了整整一下午。。~~~~(>_<)~~~~

    View Code
    #include<stdio.h>
    #include
    <iostream>
    #include
    <string.h>
    #include
    <algorithm>
    using namespace std;
    const int MAXN = 100000+10;
    typedef __int64 LL;
    long tree[22][MAXN],toleft[22][MAXN];
    LL lsum[
    22][MAXN];//lsum[deep][i]为第deep层到第xi被划入左子树的数字的和
    LL sum[MAXN];//sum[i]为1。。i的和
    long sorted[MAXN];//已经排好序的数据
    LL suml;
    int lnum;
    void build_tree(int left,int right,int deep)//建树
    {
    if(left==right)return;
    int mid = (left+right)>>1;
    int i;
    int same = mid-left+1;//位于左子树的数据
    for(i=left;i<=right;i++) //计算放于左子树中与中位数相等的数字个数
    {
    if(tree[deep][i]<sorted[mid])
    same
    --;
    }

    int ls = left;
    int rs = mid+1;
    for(i=left;i<=right;i++)
    {
    int flag = 0;
    if((tree[deep][i]<sorted[mid])||(tree[deep][i]==sorted[mid]&&same>0))//放入左子树
    {
    flag
    = 1;
    tree[deep
    +1][ls++] = tree[deep][i];
    if(tree[deep][i]==sorted[mid])same--;
    lsum[deep][i]
    =lsum[deep][i-1]+tree[deep][i];
    }
    else//右子树
    {
    tree[deep
    +1][rs++] = tree[deep][i];
    lsum[deep][i]
    =lsum[deep][i-1];
    }

    toleft[deep][i]
    =toleft[deep][i-1]+flag;
    }

    build_tree(left,mid,deep
    +1);
    build_tree(mid
    +1,right,deep+1);
    }

    int query(int left,int right,int k,int L,int R,int deep)
    {
    if(left==right)return tree[deep][left];
    int mid = (L+R)>>1;
    int x = toleft[deep][left-1]-toleft[deep][L-1];//位于left左边的放于左子树中的数字个数
    int y = toleft[deep][right] - toleft[deep][L-1];//到right为止位于左子树的个数
    int ry = right-L-y;//到right右边为止位于右子树的数字个数
    int cnt = y-x;//[left,right]区间内放到左子树中的个数
    int rx = left-L-x;//left左边放在右子树中的数字个数
    if(cnt>=k)
    return query(L+x,L+y-1,k,L,mid,deep+1);
    else
    {
    lnum
    =lnum+cnt;
    suml
    =suml+lsum[deep][right]-lsum[deep][left-1];
    return query(mid+rx+1,mid+1+ry,k-cnt,mid+1,R,deep+1);
    }

    }




    int main()
    {
    // freopen("E:/MYACM/Match/UESTC-4/Minimum Sum/1.in","r",stdin);
    // freopen("2.out","w",stdout);
    int n,m;
    int T;
    int cases = 0;
    while(scanf("%d",&T)!=EOF)
    {
    cases
    = 0;
    while(T--)
    {
    cases
    ++;
    scanf(
    "%d",&n);
    int i;
    for(i=0;i<20;i++)
    lsum[i][
    0]=0;
    sum[
    0]=0;
    for(i=1;i<=n;i++)
    {
    scanf(
    "%d",&sorted[i]);
    tree[
    0][i] = sorted[i];
    sum[i]
    =sum[i-1]+sorted[i];
    }

    sort(sorted
    +1,sorted+n+1);
    build_tree(
    1,n,0);

    scanf(
    "%d",&m);
    printf(
    "Case #%d:\n",cases);
    while(m--)
    {
    int left,right,k;
    scanf(
    "%d%d",&left,&right);
    left
    ++;
    right
    ++;
    k
    =((right-left)/2)+1;
    suml
    = 0;
    lnum
    = 0;
    long ave = query(left,right,k,1,n,0);//求中位数
    int rnum = (right-left+1-lnum);

    LL sumr
    = sum[right]-sum[left-1]-suml;//右子树的和
    LL ans = sumr-ave*(rnum-lnum) - suml;
    printf(
    "%I64d\n",ans);
    }
    printf(
    "\n");
    }

    }
    return 0;
    }
  • 相关阅读:
    PHP 计算程序运行的时间
    PHP 简易版生成随机码
    PHP读取FLASH 文件信息
    MongoDB基本使用
    PHP实现QQ达人信息抓取
    bjtuOJ 1188 素数筛选
    bjtuOJ 1139 Longest Common Subsequence
    BJTU1113扫雷问题
    C#线程池的使用详解
    C#域名解析的简单制作
  • 原文地址:https://www.cnblogs.com/AndreMouche/p/1971775.html
Copyright © 2011-2022 走看看