菜鸟系列——划分树

来源:互联网 发布:生辰八字起名软件2017 编辑:程序博客网 时间:2024/06/06 08:44

做回菜鸟,老老实实重新学起:

划分树

数据结构;

求k大值及左右和模版:

#define N 123456int sorted[N]={0};   //对原集合中元素排序后的值int val[20][N]={0};  //val记录第k层当前位置的值int num[20][N]={0}; //记录元素所在区间当前位置前的元素进入到左子树的个数int lnum, rnum;         //询问区间里面k-th数左侧和右侧的数的个数long long sum[20][N]={0};    //记录比当前元素小的元素的和long long lsum, rsum;   //询问区间里面k-th数左侧数之和与右侧数之和void build(int l, int r, int d){    if (l == r) return ;    int mid = (l + r) >> 1;    int same = mid - l + 1;    for (int i=l; i<=r; i++)        if (val[d][i] > sorted[mid])            same--;    int lp = l, rp = mid+1;    for (int i=l; i<=r; i++) {        if (i == l) {            num[d][i] = 0;            sum[d][i] = 0;        } else {            num[d][i] = num[d][i-1];            sum[d][i] = sum[d][i-1];        }        if (val[d][i] > sorted[mid]) {            num[d][i]++;            sum[d][i] += val[d][i];            val[d+1][lp++] = val[d][i];        } else if (val[d][i] < sorted[mid])            val[d+1][rp++] = val[d][i];        else {            if (same) {                same--;                num[d][i]++;                sum[d][i] += val[d][i];                val[d+1][lp++] = val[d][i];            } else val[d+1][rp++] = val[d][i];        }    }    build(l, mid, d+1);    build(mid+1, r, d+1);}int query(int a, int b, int k, int l, int r, int d){    if (a == b)         return val[d][a];    int mid = (l + r) >> 1;    int s, ss;    long long sss;    if (a == l)     {        s = num[d][b];        ss = 0;        sss = sum[d][b];    }     else     {        s = num[d][b] - num[d][a-1];        ss = num[d][a-1];        sss = sum[d][b] - sum[d][a-1];    }    if (s >= k)     {        a = l + ss;        b = l + ss + s - 1;        return query(a, b, k, l, mid, d+1);    }     else     {        lnum += s;        lsum += sss;        a = mid+1 + a - l - ss;        b = mid+1 + b - l - num[d][b];        return query(a, b, k-s, mid+1, r, d+1);    }}bool cmp(int a,int b){   return a>b; }void solve(int n,int m){    long long s[N]={0};    for(int i=1;i<=n;i++)    {        scanf("%d",&sorted[i]);        val[0][i] = sorted[i];        s[i] = s[i-1] + sorted[i];    }    printf("\n");    sort(sorted+1,sorted+1+n,cmp);    build(1,n,0);    int x,y,k,res;    while(m--)    {        scanf("%d%d%d",&x,&y,&k);        lsum = lnum = 0;        res = query(x,y,k,1,n,0);        printf("%d ",res);  //输出第k大值        rnum = y-x+1 - lnum;        rsum = s[y] - s[x-1] - lsum - res;        printf("%lld %lld\n",lsum,rsum);    //区间内比第k大值小的和大的值的和;    }}

eg:

题目参考:http://blog.csdn.net/yang_7_46/article/details/8613461


POJ 2104 k-th number

http://poj.org/problem?id=2104

题意:

求区间内k小值;

思路:

直接划分树,求第k小值;

code:

#define N 123456int sorted[N]={0};int val[20][N]={0};int num[20][N]={0};int lnum, rnum;long long sum[20][N]={0};long long lsum, rsum;void build(int l, int r, int d){    if (l == r) return ;    int mid = (l + r) >> 1;    int same = mid - l + 1;    for (int i=l; i<=r; i++)        if (val[d][i] < sorted[mid])            same--;    int lp = l, rp = mid+1;    for (int i=l; i<=r; i++) {        if (i == l) {            num[d][i] = 0;            sum[d][i] = 0;        } else {            num[d][i] = num[d][i-1];            sum[d][i] = sum[d][i-1];        }        if (val[d][i] < sorted[mid]) {            num[d][i]++;            sum[d][i] += val[d][i];            val[d+1][lp++] = val[d][i];        } else if (val[d][i] > sorted[mid])            val[d+1][rp++] = val[d][i];        else {            if (same) {                same--;                num[d][i]++;                sum[d][i] += val[d][i];                val[d+1][lp++] = val[d][i];            } else val[d+1][rp++] = val[d][i];        }    }    build(l, mid, d+1);    build(mid+1, r, d+1);}int query(int a, int b, int k, int l, int r, int d) {    if (a == b) return val[d][a];    int mid = (l + r) >> 1;    int s, ss;    long long sss;    if (a == l) {        s = num[d][b];        ss = 0;        sss = sum[d][b];    } else {        s = num[d][b] - num[d][a-1];        ss = num[d][a-1];        sss = sum[d][b] - sum[d][a-1];    }    if (s >= k) {        a = l + ss;        b = l + ss + s - 1;        return query(a, b, k, l, mid, d+1);    } else {        lnum += s;        lsum += sss;        a = mid+1 + a - l - ss;        b = mid+1 + b - l - num[d][b];        return query(a, b, k-s, mid+1, r, d+1);    }}bool cmp(int a,int b){   return a<b; }void solve(int n,int m){    for(int i=1;i<=n;i++)    {        scanf("%d",&sorted[i]);        val[0][i] = sorted[i];    }    sort(sorted+1,sorted+1+n,cmp);    build(1,n,0);    int x,y,k,res;    while(m--)    {        scanf("%d%d%d",&x,&y,&k);        res = query(x,y,k,1,n,0);        printf("%d\n",res);    }}int main() {    int n, m;    #ifndef ONLINE_JUDGE        freopen("test.txt","r",stdin);    #endif    scanf("%d%d",&n,&m);    solve(n,m);    return 0;}


HDU 3473 Minimum Sum

http://acm.hdu.edu.cn/showproblem.php?pid=3473

题意:

求区间内sum(x-xi)的最小值;x属于该区间;

思路:

找到区间内中位数即可,求区间内中位数,记录区间内大于和小于中位数的数的和,差值加减多余的中位数的值就是答案了;

code:

#define N 123456int sorted[N]={0};int val[20][N]={0};int num[20][N]={0};int lnum, rnum;long long sum[20][N]={0};long long lsum, rsum;void build(int l, int r, int d){    if (l == r) return ;    int mid = (l + r) >> 1;    int same = mid - l + 1;    for (int i=l; i<=r; i++)        if (val[d][i] < sorted[mid])            same--;    int lp = l, rp = mid+1;    for (int i=l; i<=r; i++) {        if (i == l) {            num[d][i] = 0;            sum[d][i] = 0;        } else {            num[d][i] = num[d][i-1];            sum[d][i] = sum[d][i-1];        }        if (val[d][i] < sorted[mid]) {            num[d][i]++;            sum[d][i] += val[d][i];            val[d+1][lp++] = val[d][i];        } else if (val[d][i] > sorted[mid])            val[d+1][rp++] = val[d][i];        else {            if (same) {                same--;                num[d][i]++;                sum[d][i] += val[d][i];                val[d+1][lp++] = val[d][i];            } else val[d+1][rp++] = val[d][i];        }    }    build(l, mid, d+1);    build(mid+1, r, d+1);}int query(int a, int b, int k, int l, int r, int d) {    if (a == b)         return val[d][a];    int mid = (l + r) >> 1;    int s, ss;    long long sss;    if (a == l)     {        s = num[d][b];        ss = 0;        sss = sum[d][b];    }     else     {        s = num[d][b] - num[d][a-1];        ss = num[d][a-1];        sss = sum[d][b] - sum[d][a-1];    }    if (s >= k)     {        a = l + ss;        b = l + ss + s - 1;        return query(a, b, k, l, mid, d+1);    }     else     {        lnum += s;        lsum += sss;        a = mid+1 + a - l - ss;        b = mid+1 + b - l - num[d][b];        return query(a, b, k-s, mid+1, r, d+1);    }}bool cmp(int a,int b){   return a<b; }void solve(int n,int m){    long long s[N]={0};    for(int i=1;i<=n;i++)    {        scanf("%d",&sorted[i]);        val[0][i] = sorted[i];        s[i] = s[i-1] + sorted[i];    }    scanf("%d",&m);    sort(sorted+1,sorted+1+n,cmp);    build(1,n,0);    int x,y,k,res;    while(m--)    {        scanf("%d%d",&x,&y);        x++,y++;        k = (y-x+2)>>1;        lsum = lnum = 0;        res = query(x,y,k,1,n,0);        rnum = y-x+1 - lnum;        rsum = s[y] - s[x-1] - lsum;        printf("%lld\n",(long long)(rsum-lsum+(lnum-rnum)*res));    //区间内比第k大值小的和大的值的和;    }    printf("\n");}int main(){    int k,kk,n,m;    #ifndef ONLINE_JUDGE        freopen("test.txt","r",stdin);    #endif    scanf("%d",&k);    kk=0;    while(k--)    {        printf("Case #%d:\n",++kk);        scanf("%d",&n);        solve(n,0);    }    return 0;}


0 0
原创粉丝点击