您的位置:首页 > 其它

菜鸟系列——划分树

2015-06-18 00:36 337 查看

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

划分树

数据结构;

求k大值及左右和模版:

#define N 123456
int sorted
={0};   //对原集合中元素排序后的值
int val[20]
={0};  //val记录第k层当前位置的值
int num[20]
={0}; //记录元素所在区间当前位置前的元素进入到左子树的个数
int lnum, rnum;         //询问区间里面k-th数左侧和右侧的数的个数
long long sum[20]
={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
={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:

题目参考:/article/2498566.html




POJ 2104 k-th number

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

题意:

求区间内k小值;

思路:

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

code:

#define N 123456
int sorted
={0};
int val[20]
={0};
int num[20]
={0};
int lnum, rnum;
long long sum[20]
={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 123456
int sorted
={0};
int val[20]
={0};
int num[20]
={0};
int lnum, rnum;
long long sum[20]
={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
={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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: