关闭

线段树专辑

335人阅读 评论(0) 收藏 举报
分类:

代码风格:

  • maxn是题目给的最大区间,而节点数要开4倍,确切的来说节点数要开大于maxn的最小2x的两倍
  • lson和rson分辨表示结点的左儿子和右儿子,由于每次传参数的时候都固定是这几个变量,所以可以用预定于比较方便的表示
  • 以前的写法是另外开两个个数组记录每个结点所表示的区间,其实这个区间不必保存,一边算一边传下去就行,只需要写函数的时候多两个参数,结合lson和rson的预定义可以很方便
  • PushUP(int rt)是把当前结点的信息更新到父结点
  • PushDown(int rt)是把当前结点的信息更新给儿子结点
  • rt表示当前子树的根(root),也就是当前所在的结点


hdu 1166

线段树功能:update:单点增减 query:区间求和

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define lson l , m , rt << 1
#define rson m+1 , r , rt << 1 | 1

const int maxn = 55555;
int sum[maxn<<2];

void PushUP(int rt)
{
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void build(int l,int r,int rt)
{
    int m;

    if(l == r)
    {
        scanf("%d",&sum[rt]);
        return ;
    }
    m = (l + r) >> 1;
    build(lson);
    build(rson);
    PushUP(rt);
}

void update(int p,int add,int l,int r,int rt)
{
    int m;

    if(l == r)
    {
        sum[rt] += add;
        return ;
    }
    m = (l + r) >> 1;
    if(p <= m) update(p,add,lson);
    else update(p,add,rson);
    PushUP(rt);
}

int query(int L,int R,int l,int r,int rt)
{
    int m,ret;

    if(L <= l && r<=R)
        return sum[rt];

    m = (l + r) >> 1;
    ret = 0;
    if(L <= m) ret += query(L,R,lson);
    if(R > m) ret += query(L,R,rson);

    return ret;
}

int main()
{
    int T,n,ncase,a,b;
    char op[10];

    scanf("%d",&T);
    for(ncase=1;ncase<=T;ncase++)
    {
        scanf("%d",&n);
        build(1,n,1);
        printf("Case %d:\n",ncase);
        while(scanf("%s",&op))
        {
            if(op[0] == 'E') break;
            scanf("%d%d",&a,&b);
            if(op[0] == 'Q')
                printf("%d\n",query(a,b,1,n,1));
            else if(op[0] == 'S')
                update(a,-b,1,n,1);
            else
                update(a,b,1,n,1);
        }
    }

    return 0;
}

hdu 1754  I hate it

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define lson l , m , rt<<1
#define rson m+1 , r , rt<<1|1

const int MAXN = 200010;
int sum[MAXN<<2];

int max(int a,int b)
{
    return a > b ? a : b;
}

void PushUP(int rt)
{
    sum[rt] = max(sum[rt<<1] , sum[rt<<1|1]);
}

void build(int l,int r,int rt)
{
    int m;

    if(l == r)
    {
        scanf("%d",&sum[rt]);
        return ;
    }

    m = (l + r) >> 1;
    build(lson);
    build(rson);
    PushUP(rt);
}

int query(int L,int R,int l,int r,int rt)
{
    int m,ans = 0;

    if(L <= l && r <= R)
    {
        return sum[rt];
    }
    m = (l + r) >> 1;
    if(L <= m) ans = max(ans,query(L,R,lson));
    if(R > m) ans = max(ans,query(L,R,rson));

    return ans;
}

void update(int pos,int value,int l,int r,int rt)
{
    int m;

    if(l == r)
    {
        sum[rt] = value;
        return ;
    }
    m = (l + r) >> 1;
    if(pos <= m)
        update(pos,value,lson);
    else
        update(pos,value,rson);

    PushUP(rt);
}

int main()
{
    int N,M,a,b,i;
    char op;
    while(~scanf("%d%d",&N,&M))
    {
        build(1,N,1);
        getchar();
        for(i = 0;i < M;i ++)
        {
            scanf("%c %d %d",&op,&a,&b);
            if(op == 'Q')
                printf("%d\n",query(a,b,1,N,1));
            else
                update(a,b,1,N,1);
            getchar();
        }
    }

    return 0;
}

hdu 1394

题意:求Inversion后的最小逆序数
思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解
线段树功能:update:单点增减 query:区间求和

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define lson l , m , rt << 1
#define rson m+1 , r , rt << 1 | 1

const int maxn = 5555;
int sum[maxn<<2];

void PushUP(int rt)
{
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void build(int l,int r,int rt)
{
    int m;

    if(l == r)
    {
        sum[rt] = 0;
        return ;
    }

    m = (l + r) >> 1;
    build(lson);
    build(rson);
    PushUP(rt);
}

int query(int L,int R,int l,int r,int rt)
{
    int m,ans = 0;

    if(L <= l && r <= R)
        return sum[rt];
    m = (l + r) >> 1;
    if(L <= m) ans += query(L,R,lson);
    if(R > m) ans += query(L,R,rson);

    return ans;
}

void update(int pos,int value,int l,int r,int rt)
{
    int m;

    if(l == r)
    {
        sum[rt] = value;
        return ;
    }
    m = (l + r) >> 1;
    if(pos <= m)
        update(pos,value,lson);
    else
        update(pos,value,rson);

    PushUP(rt);
}

int min(int a,int b)
{
    return a > b ? b : a;
}

int x[maxn];

int main()
{
    int n,i,sum,ans;

    while(~scanf("%d",&n))
    {
        sum = 0;
        build(1,n,1);
        for(i = 0;i < n;i ++)
        {
            scanf("%d",&x[i]);
            sum += query(x[i] + 1,n,1,n,1);
            update(x[i] + 1,1,1,n,1);
        }

        ans = 10000000;
        for(i = 0;i < n;i ++)
        {
            sum += n - x[i] - x[i] -1;
            ans = min(sum,ans);
        }
        printf("%d\n",ans);
    }

    return 0;
}

hdu 2795

这题有点DT,一开始我以为是一个复杂的DP,但是其实不是 == ,只要每次找到最大值的位子,然后减去L就可以了 =。=

比如,对于输入:

2 6 4

3/2/4/3

输出为:

1/1/2/-1,

而不是2/1/1/2   =。=

只是个贪心 = =

# include <stdio.h>

#define lson l,m,rt << 1
#define rson m + 1,r,rt << 1|1

const int maxn = 200010;
int MAX[maxn<<2];

int h,w,n;

int max(int a,int b)
{
    return a > b ? a : b;
}

void PushUP(int rt)
{
    MAX[rt] = max(MAX[rt << 1],MAX[rt << 1|1]);

}

void build(int l,int r,int rt)
{
    int m;

    if(l == r)
    {
        MAX[rt] = w;
        return ;
    }
    m = (l + r) >> 1;
    build(lson);
    build(rson);
    PushUP(rt);
}

int query(int x,int l,int r,int rt)
{
    int m,ans;

    if(l == r)
    {
        MAX[rt] -= x;
        return l;
    }
    m = (l + r) >> 1;
    ans = (MAX[rt << 1] >= x) ? query(x,lson) : query(x,rson);

    PushUP(rt);

    return ans;
}

int main()
{
    int i,x;

    while(~scanf("%d%d%d",&h,&w,&n))
    {
        if(h > n) h = n;
        build(1,h,1);
        for(i = 0;i < n;i ++)
        {
            scanf("%d",&x);
            if(x > MAX[1])
                puts("-1");
            else
                printf("%d\n",query(x,1,h,1));
        }
    }

    return 0;
}

poj 2828

给定n个人进入队伍时的位置,如果某个位置及其后面有人,则后面的人都要向后挪一个位置,这不正是每天都在现实生活中上演的插队问题吗!有n个pos[i]和val[i],pos[i]表示这个人插入到pos[i]的右面,其实加1的话,更好理解,就是插在什么位置。至于那个val[i]只是为了表示某个人而已,理解成RP什么的都可以。


换个角度,后插进来的人更容易定位,比如最后一个人插在什么位置就是在什么位置,不需要挪动,这也很容易理解。现在从后面开始定位每个人的位置,不失一般性,如果某个人插入到pos[i],则找第pos[i]个空位置插入即可,记录空位置可以通过线段树实现


# include <stdio.h>

#define lson l , m , rt<<1
#define rson m+1 , r , rt<<1|1
const int maxn = 200010;

int empty[maxn<<2];
int ans[maxn],pos[maxn],val[maxn];
int index;

void build(int l,int r,int rt)
{
    int m;

    empty[rt] = r - l + 1;
    if(l == r)
        return ;
    m = (l + r) >> 1;
    build(lson);
    build(rson);
}

void update(int p,int l,int r,int rt)
{
    int m;

    empty[rt] --;
    if(l == r)
    {
        index = l;
        return ;
    }
    m = (l + r) >> 1;
    if(empty[rt<<1] >= p)
        update(p,lson);
    else
        p -= empty[rt<<1],update(p,rson);
}

int main()
{
    int n,i;

    while(~scanf("%d",&n))
    {
        build(1,n,1);
        for(i = 1;i <= n;i ++)
            scanf("%d%d",&pos[i],&val[i]);
        for(i = n;i >= 1;i --)
            update(pos[i]+1,1,n,1),ans[index] = val[i];
        for (i = 1; i <= n; ++i)
            printf(i == n ? "%d\n" : "%d ",ans[i]);
    }

    return 0;
}


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:52653次
    • 积分:1793
    • 等级:
    • 排名:千里之外
    • 原创:135篇
    • 转载:0篇
    • 译文:0篇
    • 评论:2条
    最新评论