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域,num[i]表示lft->i这个点有多少个进入了左子树。

划分树的Sample

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int maxf=20;
int order[maxn];
int v[maxf][maxn];
ll s[maxn];
ll sum[maxf][maxn],asum;
int num[maxf][maxn];
void build(int l,int r,int ind){
        if(l==r)return ;
        int mid=(l+r)>>1;
        int ln=l,rn=mid+1,same=mid-l+1;
        for(int i=l;i<=r;i++){
                if(v[ind][i]<order[mid])same--;
        }
        for(int i=l;i<=r;i++){
                int flag=0;
                if(v[ind][i]==order[mid]&&same>0){
                        same--;
                        flag=1;
                        v[ind+1][ln++]=v[ind][i];
                        sum[ind][i]=(i>0?sum[ind][i-1]:0)+v[ind][i];
                }
                else if(v[ind][i]<order[mid]){
                        flag=1;
                        v[ind+1][ln++]=v[ind][i];
                        sum[ind][i]=(i>0?sum[ind][i-1]:0)+v[ind][i];
                }
                else {
                        sum[ind][i]=i>0?sum[ind][i-1]:0;
                        v[ind+1][rn++]=v[ind][i];
                }
                num[ind][i]=(i>0?num[ind][i-1]:0)+flag;
        }
        build(l,mid,ind+1);
        build(mid+1,r,ind+1);
}
int query(int s,int e,int k,int l,int r,int ind){
        if(l==r)return v[ind][l];
        int mid=(l+r)>>1;
        int lls=num[ind][s-1]-num[ind][l-1];
        int lse=num[ind][e]-num[ind][s-1];
        int rls=s-l-lls;
        int rse=e-s-lse+1;
        if(lse>=k)return query(l+lls,l+lls+lse-1,k,l,mid,ind+1);
        asum+=sum[ind][e]-(s>0?sum[ind][s-1]:0);
        return  query(mid+1+rls,mid+rls+rse,k-lse,mid+1,r,ind+1);
}
int main(){
        int T;
        scanf("%d",&T);
        for(int t=1;t<=T;t++){
                int n;
                scanf("%d",&n);
                for(int i=0;i<n;i++){
                        scanf("%d",&v[0][i]);
                        order[i]=v[0][i];
                        s[i]=(i>0?s[i-1]:0)+v[0][i];
                }
                sort(order,order+n);
                memset(num,0,sizeof(num));
                build(0,n-1,0);
                int q;
                scanf("%d",&q);
                printf("Case #%d:\n",t);
                for(int i=0;i<q;i++){
                        int l,r;
                        scanf("%d%d",&l,&r);
                        asum=0;
                        ll mid=query(l,r,((l+r)>>1)-l+1,0,n-1,0);
                        ll ans=mid*(((l+r)>>1)-l)-(r-((l+r)>>1))*mid-asum+(s[r]-(l>0?s[l-1]:0)-asum-mid);
                        printf("%I64d\n",ans);
                }
                puts("");
        }
}

  

时间: 2024-11-10 05:23:23

HDU 3473 Minimum Sum 划分树,数据结构 难度:1的相关文章

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 划分树

题目大意:给定一个序列,每次询问给出一个区间,我们需要选择一个数,这个数到区间内所有数的距离之和最小,求最小和 由绝对值不等式可得 当我们选择的这个数是中位数的时候距离和最小 于是这题就转换成了区间第k小 但是这题求的是最小和 于是我们做一个处理 我们多维护一个sum域 sum[i]表示[l,i]区间内划分到左子树中元素的总和 然后我们每次查询第k小时 如果我们进入的是右子树 就把划分到左子树中的元素和累加到left_sum上 然后用前缀和计算出区间的和 计算出right_sum 最后的结果就是

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 3473 Minimum Sum

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

HDU 1394 Minimum Inversion Number (数据结构-线段树)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 9514    Accepted Submission(s): 5860 Problem Description The inversion number of a given number sequence a1, a2, ..., an

HDU 1394 Minimum Inversion Number 树状数组&amp;&amp;线段树

题目给了你一串序列,然后每次 把最后一个数提到最前面来,直到原来的第一个数到了最后一个,每次操作都会产生一个新的序列,这个序列具有一个逆序数的值,问最小的你逆序数的值为多少 逆序数么 最好想到的是树状数组,敲了一把很快,注意把握把最后一个数提上来对逆序数的影响即可, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #i

HDU 4825 Xor Sum 字典树+位运算

点击打开链接 Xor Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others) Total Submission(s): 291    Accepted Submission(s): 151 Problem Description Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus

hdu 4828 Xor Sum (trie 树模板题,经典应用)

hdu 4825 题目链接 题意:给定n个数,然后给出m个询问,每组询问一个数x,问n中的数y使得x和y的异或和最大. 思路:字典树..把每个数转化成二进制,注意补全前导0,使得所有数都有相同的位数. 如果想要异或和最大,那么每一位尽可能都是1. 所以做法是,先构建字典树,然后每次find的时候,尽可能按照和当前寻找的数的位相反的位的方向走(如果有的话) 比如当前位是1,那我就往0的方向走. 需要注意的是,多组数据,每次要重新初始化一遍. 做法是 在struct 中重新 root = new N