菜鸟系列——线段树

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

线段树

基本数据结构;

求和模版:

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

int sum[N<<2],add[N<<2];
void pushUp(int rt) {
    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void pushDown(int l,int r,int rt) {
    if(add[rt]) {
        int m = (l+r)>>1;
        add[rt<<1] += add[rt];
        add[rt<<1|1] += add[rt];
        sum[rt<<1] += (m-l+1)*add[rt];
        sum[rt<<1|1] += (r-m)*add[rt];
        add[rt] = 0;
    }
}
void updata(int l,int r,int rt,int ql,int qr,int val) {
    if(l>qr||ql>r)return;
    if(l>=ql&&r<=qr) {
        sum[rt] += (r-l+1)*val;
        add[rt] += val;
        return;
    }
    pushDown(l,r,rt);
    int m = (l+r)>>1;
    if(ql<=m)updata(lson,ql,qr,val);
    if(qr>m)updata(rson,ql,qr,val);
    pushUp(rt);
}
void build(int l,int r,int rt) {
    add[rt]=0;
    if(l == r) {
        scanf("%d",&sum[rt]);
        return;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    pushUp(rt);
}
int query(int l,int r,int rt,int ql,int qr) {
    if(l>qr||ql>r)
        return 0;
    if(l>=ql&&r<=qr)
        return sum[rt];
    pushDown(l,r,rt);
    int m = (l+r)>>1;
    return query(lson,ql,qr)+query(rson,ql,qr);
}



 

eg:

题目参考:http://blog.csdn.net/metalseed/article/details/8039326 

HDU1166 敌兵布阵

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

题意:

求区间和,单点加和更新;

思路:

线段树区间求和模版题;

code:
#define N 51234
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
long long sum[N<<2],add2[N<<2];
void pushUp(int rt)
{
    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void pushDown2(int l,int r,int rt)//加和操作的延迟标记
{
    if(add2[rt])
    {
        int m = l+r>>1;
        add2[rt<<1] += add2[rt];
        add2[rt<<1|1] += add2[rt];
        sum[rt<<1] += (m-l+1)*add2[rt];
        sum[rt<<1|1] += (r-m)*add2[rt];
        add2[rt] = 0;
    }
}
void update2(int l,int r,int rt,int ql,int qr,int val)//加和操作
{
    if(l>qr||ql>r)return;
    if(l>=ql&&r<=qr)
    {
        sum[rt] += (r-l+1)*val;
        add2[rt] += val;
        return;
    }
    pushDown2(l,r,rt);
    int m = l+r>>1;
    if(ql<=m)update2(lson,ql,qr,val);
    if(qr>m)update2(rson,ql,qr,val);
    pushUp(rt);
}
void build(int l,int r,int rt)
{
    add2[rt]=0;
    if(l == r)
    {
        scanf("%lld",&sum[rt]);
        return;
    }
    int m = l+r>>1;
    build(lson);
    build(rson);
    pushUp(rt);
}
long long query(int l,int r,int rt,int ql,int qr)
{
    if(l>qr||ql>r)
        return 0;
    if(l>=ql&&r<=qr)
        return sum[rt];
    pushDown2(l,r,rt);
    int m = l+r>>1;
    return query(l,m,rt<<1,ql,qr)+query(m+1,r,rt<<1|1,ql,qr);
}

int main()
{
    int i,j,k,kk,t,x,y;
    #ifndef ONLINE_JUDGE
        freopen("test.txt","r",stdin);
    #endif
    scanf("%d",&k);
    kk=0;
    while(k--)
    {
        printf("Case %d:\n",++kk);
        scanf("%d",&n);
        build(root);
        while(scanf("%s",s) && s[0]!='E')
        {
            scanf("%d %d",&x,&y);
            if(s[0] == 'Q')
                printf("%d\n",query(root,x,y));
            else if(s[0] == 'A')
                update2(root,x,x,y);
            else
                update2(root,x,x,-y);
        }
    }
    return 0;
}

HDU1754 I hate it

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

题意:

求区间最大值,单点更新;

思路:

线段树区间求最值模版题;

code:
#define N 200000
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
int sum[N<<2],add1[N<<2];
void pushUp(int rt)
{
    sum[rt] = max(sum[rt<<1],sum[rt<<1|1]);
}
void pushDown1(int l,int r,int rt)//更新操作的延迟标记
{
    if(add1[rt])
    {
        int m = l+r>>1;
        add1[rt<<1] = add1[rt<<1|1] = add1[rt];
        sum[rt<<1] = max(add1[rt],sum[rt<<1]);
        sum[rt<<1|1] = max(add1[rt],sum[rt<<1|1]);
        add1[rt] = 0;
    }
}
void update1(int l,int r,int rt,int ql,int qr,int val)//更新操作
{
    if(l>qr||ql>r)return;
    if(l>=ql&&r<=qr)
    {
        sum[rt] = max(sum[rt],val);
        add1[rt] = val;
        return;
    }
    pushDown1(l,r,rt);
    int m = l+r>>1;
    if(ql<=m)update1(lson,ql,qr,val);
    if(qr>m)update1(rson,ql,qr,val);
    pushUp(rt);
}
void build(int l,int r,int rt)
{
    add1[rt]=0;
    if(l == r)
    {
        scanf("%lld",&sum[rt]);
        return;
    }
    int m = l+r>>1;
    build(lson);
    build(rson);
    pushUp(rt);
}
long long query(int l,int r,int rt,int ql,int qr)
{
    if(l>qr||ql>r)
        return 0;
    if(l>=ql&&r<=qr)
        return sum[rt];
    pushDown1(l,r,rt);
    int m = l+r>>1;
    return max(query(l,m,rt<<1,ql,qr),query(m+1,r,rt<<1|1,ql,qr));
}

int main()
{
    int i,j,k,kk,t,x,y;
    #ifndef ONLINE_JUDGE
        freopen("test.txt","r",stdin);
    #endif
    while(scanf("%d %d",&n,&m)!=EOF&&n)
    {
        build(root);
        for(i=0;i<m;i++)
        {
            scanf("%s %d %d",s,&x,&y);
            if(s[0] == 'Q')
                printf("%d\n",query(root,x,y));
            else
                update1(root,x,x,y);
        }
    }
    return 0;
}

HDU1394 Minimum Inversion Number

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

题意:

求该数列把头移到尾的所有情况的逆序数最小值;

思路:

用线段树依次加入求出初始逆序数;

把第一个数移到最后的新逆序数就是:

原来逆序数加上第一个数后面大于的数,减去后面更小的数;

又因为数列为0到n-1,

res = res+(n-num[i]-1)-(num[i]);

改为1到n时为:

res = res+(n-num[i])-(num[i]-1);

code:
#define N 51234
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

long long sum[N<<2],add1[N<<2],num[N];

//向上更新操作
void pushUp(int rt)
{
    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}

//更新操作的延迟标记

void pushDown1(int l,int r,int rt)
{
    if(add1[rt])
    {
        int m = l+r>>1;
        add1[rt<<1] = add1[rt<<1|1] = add1[rt];
        sum[rt<<1] = (m-l+1)*add1[rt];
        sum[rt<<1|1] = (r-m)*add1[rt];
        add1[rt] = 0;
    }
}
//区间更新操作
void update1(int l,int r,int rt,int ql,int qr,int val)
{
    if(l>qr||ql>r)return;
    if(l>=ql&&r<=qr)
    {
        sum[rt] = (r-l+1)*val;
        add1[rt] = val;
        return;
    }
    pushDown1(l,r,rt);
    int m = l+r>>1;
    if(ql<=m)update1(lson,ql,qr,val);
    if(qr>m)update1(rson,ql,qr,val);
    pushUp(rt);
}
//区间和询问
long long query(int l,int r,int rt,int ql,int qr)
{
    if(l>qr||ql>r)
        return 0;
    if(l>=ql&&r<=qr)
        return sum[rt];
    pushDown1(l,r,rt);
    int m = l+r>>1;
    return query(l,m,rt<<1,ql,qr)+query(m+1,r,rt<<1|1,ql,qr);
}

int main()
{
    int i,j,k,kk,t,x,y,res,ans;
    #ifndef ONLINE_JUDGE
        freopen("test.txt","r",stdin);
    #endif
    while(scanf("%d",&n)!=EOF&&n)
    {
        memset(sum,0,sizeof(sum));
        memset(add1,0,sizeof(add1));
        res = 0;
        //依次加入,求出最初逆序数
        for(i=1;i<=n;i++)
        {
            scanf("%d",&num[i]);
            num[i]+=1;//原来数是从0到n-1
            res += query(root,num[i],n);
            update1(root,num[i],num[i],1);
        }
        ans = res;
        //num[i]从头移到尾,逆序数加上num[i]后面小于num[i]的数n-num[i];
        //再减去num[i]后面大于num[i]的数num[i]-1;
        for(i=1;i<n;i++)
        {
            res = res+n-num[i]-num[i]+1;
            ans = min(ans,res);
        }
        printf("%d\n",ans);
    }
    return 0;
}



HDU2795 Billboard

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

题意:

在一个board上放(1,w)的传单,求能放到的最高处;

思路:
把board竖过来存成线段树,对于每个传单找大于wi的位置,再减去wi;
code:
#define N 212345
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

int sum[N<<2];

//向上更新操作
void pushUp(int rt)
{
    sum[rt] = max(sum[rt<<1],sum[rt<<1|1]);
}
//建树
void build(int l,int r,int rt)
{
    sum[rt] = w;
    if(l == r)
        return;
    int m = l+r>>1;
    build(lson);
    build(rson);
}
long long query(int l,int r,int rt,int x)
{
    if(l == r)
    {
        //询问同时更新操作
        sum[rt] -= x;
        return l;
    }
    int m = (l+r)>>1;
    int res = (sum[rt<<1]>=x)?query(lson,x):query(rson,x);
    pushUp(rt);
    return res;
}

int main()
{
    int i,j,k,kk,t,x,y,res,ans;
    #ifndef ONLINE_JUDGE
        freopen("test.txt","r",stdin);
    #endif
    while(scanf("%d%d%d",&n,&w,&m)!=EOF&&n)
    {
        if(n>m)//利用公告总数小于200000,否则mlt
            n = m;
        build(root);
        for(i=0;i<m;i++)
        {
            scanf("%d",&t);
            if(sum[1]<t)
                printf("-1\n");
            else
                printf("%d\n",query(root,t));
        }
    }
    return 0;
}


HDU1698 Just a Hook

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

题意:

区间更新求总和;

思路:
直接线段树区间更新模版;
code:
#define N 112345
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

int  sum[N<<2],add1[N<<2];
void pushUp(int rt)
{
    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void pushDown1(int l,int r,int rt)
{
    if(add1[rt])
    {
        int m = (l+r)>>1;
        add1[rt<<1] = add1[rt<<1|1] = add1[rt];
        sum[rt<<1] = (m-l+1)*add1[rt];
        sum[rt<<1|1] = (r-m)*add1[rt];
        add1[rt] = 0;
    }
}
void update1(int l,int r,int rt,int ql,int qr,int val)
{
    if(l>qr||ql>r)return;
    if(l>=ql&&r<=qr)
    {
        sum[rt] = (r-l+1)*val;
        add1[rt] = val;
        return;
    }
    pushDown1(l,r,rt);
    int m = (l+r)>>1;
    if(ql<=m)update1(lson,ql,qr,val);
    if(qr>m)update1(rson,ql,qr,val);
    pushUp(rt);
}
//建树
void build(int l,int r,int rt)
{
    add1[rt]=0;
    if(l == r)
    {
        sum[rt] = 1;
        return;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    pushUp(rt);
}
//区间和询问
int query(int l,int r,int rt,int ql,int qr)
{
    if(l>qr||ql>r)
        return 0;
    if(l>=ql&&r<=qr)
        return sum[rt];
    pushDown1(l,r,rt);
    int m = l+r>>1;
    return query(l,m,rt<<1,ql,qr)+query(m+1,r,rt<<1|1,ql,qr);
}

int main()
{
    int i,j,k,kk,t,x,y,res,ans;
    #ifndef ONLINE_JUDGE
        freopen("test.txt","r",stdin);
    #endif
    scanf("%d",&k);
    kk=0;
    while(k--)
    {
        scanf("%d%d",&n,&m);
        build(root);
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&x,&y,&t);
            update1(root,x,y,t);
        }
        printf("Case %d: The total value of the hook is %d.\n",++kk,sum[1]);
    }
    return 0;
}

POJ3468 A Simple Problem with Integers

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

题意:

区间加和求总和;

思路:
直接线段树区间更新模版;
code:
#define N 512345
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

long long sum[N<<2],add2[N<<2];
//向上更新操作
void pushUp(int rt)
{
    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
//加和操作的延迟标记

void pushDown2(int l,int r,int rt)
{
    if(add2[rt])
    {
        int m = (l+r)>>1;
        add2[rt<<1] += add2[rt];
        add2[rt<<1|1] += add2[rt];
        sum[rt<<1] += (m-l+1)*add2[rt];
        sum[rt<<1|1] += (r-m)*add2[rt];
        add2[rt] = 0;
    }
}

//区间整体加和操作

void update2(int l,int r,int rt,int ql,int qr,int val)
{
    if(l>qr||ql>r)return;
    if(l>=ql&&r<=qr)
    {
        sum[rt] += (r-l+1)*val;
        add2[rt] += val;
        return;
    }
    pushDown2(l,r,rt);
    int m = (l+r)>>1;
    if(ql<=m)update2(lson,ql,qr,val);
    if(qr>m)update2(rson,ql,qr,val);
    pushUp(rt);
}
//建树
void build(int l,int r,int rt)
{
    add2[rt]=0;
    if(l == r)
    {
        scanf("%lld",&sum[rt]);
        return;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    pushUp(rt);
}
//区间和询问
long long query(int l,int r,int rt,int ql,int qr)
{
    if(l>qr||ql>r)
        return 0;
    if(l>=ql&&r<=qr)
        return sum[rt];
    pushDown2(l,r,rt);
    int m = l+r>>1;
    return query(l,m,rt<<1,ql,qr)+query(m+1,r,rt<<1|1,ql,qr);
}

int main()
{
    int i,j,k,kk,t,x,y,res,ans;
    #ifndef ONLINE_JUDGE
        freopen("test.txt","r",stdin);
    #endif
    while(scanf("%d%d",&n,&m)!=EOF&&n)
    {
        build(root);
        for(i=0;i<m;i++)
        {
            scanf("%s",s);
            if(s[0]=='C')
            {
                scanf("%d%d%d",&x,&y,&t);
                update2(root,x,y,t);
            }
            else
            {
                scanf("%d%d",&x,&y);
                printf("%lld\n",query(root,x,y));
            }
        }
    }
    return 0;
}

POJ2528 Mayor’s posters

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

题意:

贴海报,最后求还有几个海报能漏出来;

思路:
线段树区间更新,加上坐标离散化;

但有个BUG,直接离散化会使相邻但不紧挨的海报合并,,应该在其中加一位;

但是数据奇弱能直接过,离散化加了考虑反而RE,,具体原因不详,再议

code:
#define N 112345
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

int n,m;
int f[N][2];
struct node
{
    int x,y;
    int r,w;
    int to,next;
    void init()
    {
        r=0;
    }
    friend bool operator < (node a, node b)
    {
//        if(a.y==b.y)return a.x > b.x;
        return a.x < b.x;
    }
}tn[2*N];
int sum[N<<2],mark[N],res;

//向下更新操作
void pushDown(int rt)
{
    if(sum[rt]>0)
    {
        sum[rt<<1] = sum[rt<<1|1] = sum[rt];
        sum[rt] = 0;
    }
}
//区间更新操作

void update1(int l,int r,int rt,int ql,int qr,int val)
{
    if(l>qr||ql>r)return;
    if(l>=ql && r<=qr)
    {
        sum[rt] = val;
        return;
    }
    pushDown(rt);
    int m = (l+r)>>1;
    if(ql<=m)update1(lson,ql,qr,val);
    if(qr>m)update1(rson,ql,qr,val);
}
//建树
void build(int l,int r,int rt)
{
    sum[rt] = 0;
    if(l == r)
    {
        return;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
}
//区间询问
void query(int l,int r,int rt)
{
    if(sum[rt]>0)
    {
        if(mark[sum[rt]]==0)
            res++;
        mark[sum[rt]]=1;
        return;
    }
    if(l==r)
        return;
    query(lson);
    query(rson);
}
void discretize(int j)
{
    sort(tn,tn+j);
    int now=1;
    int t = tn[0].x;
    for(int i=0;i<j;i++)
    {
        if(tn[i].x!=t)
            now++;
//        if(tn[i].x-t>1)
//            now++;
        t = tn[i].x;
        tn[i].x = now;
        if(tn[i].y>0)
            f[tn[i].y][0] = tn[i].x;
        else
            f[-tn[i].y][1] = tn[i].x;

    }
    n=now;
}
int main()
{
    int i,j,k,kk,t,x,y;
    #ifndef ONLINE_JUDGE
        freopen("test.txt","r",stdin);
    #endif
    scanf("%d",&k);
    while(k--)
    {
        int hehe;
        scanf("%d",&hehe);
        for(i=1,j=0;i<=hehe;i++)
        {
            scanf("%d%d",&f[i][0],&f[i][1]);
            tn[j].x = f[i][0];
            tn[j++].y = i;
            tn[j].x = f[i][1];
            tn[j++].y = -i;
        }
        discretize(j);
        build(root);
        for(i=1;i<=hehe;i++)
            update1(root,min(f[i][0],f[i][1]),max(f[i][0],f[i][1]),i);
        memset(mark,0,sizeof(mark));
        res=0;
        query(root);
        printf("%d\n",res);
    }
    return 0;
}


POJ3225 Help with Intervals

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

题意:

区间操作


最后求在区间S中的所有区间

思路:

也就是:

U  [l,r] = 1;

I    [0,l) = (r,N] = 0;

D  [l,r] = 0;

C  [l,r] ^= 1;

S  [0,l) = (r,N] = 0; 然后 [l,r] ^= 1;

用线段树操作,延迟标记好异或情况就行了;

主要是情况多,,细节!细节!细节!


code:
#define N 512345
#define root 0 , N , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

int sum[N<<2],OR[N<<2];
bool mark[N+10];

//做异或操作
void doOR(int rt)
{
    if(sum[rt] != -1)sum[rt] ^= 1;
    else OR[rt] ^= 1;   //改点无色则延迟标记,向下推
}

//更新操作的延迟标记
void pushDown(int l,int r,int rt)
{
    if(sum[rt] != -1)
    {
        sum[rt<<1] = sum[rt<<1|1] = sum[rt];
        OR[rt<<1] = OR[rt<<1|1] = 0;
        sum[rt] = -1;
    }
    if(OR[rt])
    {
        doOR(rt<<1);
        doOR(rt<<1|1);
        OR[rt] = 0;
    }
}

//区间更新操作
void update(int l,int r,int rt,int ql,int qr,char val)
{
    if(l>=ql && r<=qr)
    {
        if(val == 'U')
        {
            sum[rt] = 1;
            OR[rt] = 0;
        }
        else if(val == 'D')
        {
            sum[rt] = 0;
            OR[rt] = 0;
        }
        else if(val == 'C' || val == 'S')
            doOR(rt);
        return;
    }
    pushDown(l,r,rt);
    int m = (l+r)>>1;
    if(ql<=m)update(lson,ql,qr,val);
    else if(val == 'I' || val == 'C')
        OR[rt<<1] = sum[rt<<1] = 0;
    if(qr>m)update(rson,ql,qr,val);
    else if(val == 'I' || val == 'C')
        OR[rt<<1|1] = sum[rt<<1|1] = 0;
}

//区间询问
void  query(int l,int r,int rt)
{
    if(sum[rt] == 1)
    {
        for(int i=l;i<=r;i++)
            mark[i] = true;
        return;
    }
    else if(sum[rt] == 0)
        return ;
    if(l == r)return ;
    pushDown(l,r,rt);
    int m = (l+r)>>1;
    query(lson);
    query(rson);
}

int main()
{
    int i,j,k,kk,t,x,y;
    #ifndef ONLINE_JUDGE
        freopen("test.txt","r",stdin);
    #endif
    //更新每一条信息;
    sum[1] = OR[1] = 0;
    char c,l,r;
    while(scanf("%c %c%d,%d%c\n",&c,&l,&x,&y,&r)!=EOF)
    {
        //放大二倍,确定(与[;
        x*=2;
        y*=2;
        if(l == '(')x++;
        if(r == ')')y--;
        if(x>y && (c=='C'||c=='I'))//整个区间都在范围外全部清空;
            sum[1] = OR[1] = 0;
        else
            update(root,x,y,c);
    }

    //确定区间
    memset(mark,false,sizeof(mark));
    query(root);

    //输出
    bool flag = false;
    x = y = -1;
    for(i=0;i<N;i++)
    {
        if(mark[i])
        {
            if(x == -1)
                x = i;
            y = i;
        }
        else
        {
            if(x != -1)
            {
                if(flag)printf(" ");
                printf("%c%d,%d%c",x&1?'(':'[',x>>1,(y+1)>>1,y&1?')':']');
                flag = true;
                x = y = -1;
            }
        }
    }
    if(!flag)
        printf("empty set");
    printf("\n");
    return 0;
}

POJ3667 Hotel

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

题意:

两类操作:

1 a  寻找是否有连续长度为a的空房

2 a b  将a房间开始的b个房间清空

对于1 操作输出连续空房的最左边的房间

思路:
线段树更新, 线段树区间合并操作
code:
#define N 51234
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

int sum[N<<2],lsum[N<<2],rsum[N<<2],add1[N<<2];

//向上更新操作
void pushUp(int l,int r,int rt)
{
    lsum[rt] = lsum[rt<<1];
    rsum[rt] = rsum[rt<<1|1];
    int m = r-l+1;
    if(lsum[rt] == m-(m>>1))
        lsum[rt] += lsum[rt<<1|1];
    if(rsum[rt] == (m>>1))
        rsum[rt] += rsum[rt<<1];
    sum[rt] = max(lsum[rt<<1|1]+rsum[rt<<1],max(sum[rt<<1],sum[rt<<1|1]));
}

//更新操作的延迟标记

void pushDown(int l,int r,int rt)
{
    if(add1[rt]!=-1)
    {
        int m = r-l+1;
        add1[rt<<1] = add1[rt<<1|1] = add1[rt];
        sum[rt<<1] = lsum[rt<<1] = rsum[rt<<1] = add1[rt]?0:m-(m>>1);
        sum[rt<<1|1] = lsum[rt<<1|1] = rsum[rt<<1|1] = add1[rt]?0:(m>>1);
        add1[rt] = -1;
    }
}

//区间更新操作
void update(int l,int r,int rt,int ql,int qr,int val)
{
    if(l>qr||ql>r)return;
    if(l>=ql&&r<=qr)
    {
        sum[rt] = lsum[rt] = rsum[rt] = val?0:r-l+1;
        add1[rt] = val;
        return;
    }
    pushDown(l,r,rt);
    int m = (l+r)>>1;
    if(ql<=m)update(lson,ql,qr,val);
    if(qr>m)update(rson,ql,qr,val);
    pushUp(l,r,rt);
}

//建树
void build(int l,int r,int rt)
{
    add1[rt]=-1;
    sum[rt] = lsum[rt] = rsum[rt] = r-l+1;
    if(l == r)
        return;
    int m = (l+r)>>1;
    build(lson);
    build(rson);
}
//区间询问
int query(int l,int r,int rt,int val)
{
    if(l == r)
        return l;
    pushDown(l,r,rt);
    int m = l+r>>1;
    if(sum[rt<<1] >= val)
        return query(lson,val);
    else if(rsum[rt<<1]+lsum[rt<<1|1] >= val)
        return m-rsum[rt<<1]+1;
    return query(rson,val);
}

int main()
{
    int i,j,k,kk,t,x,y,op,n,m;
    #ifndef ONLINE_JUDGE
        freopen("test.txt","r",stdin);
    #endif
    while(scanf("%d%d",&n,&m)!=EOF&&n)
    {
        build(root);
        while(m--)
        {
            scanf("%d",&op);
            if(op == 1)
            {
                scanf("%d",&x);
                if(sum[1]<x)
                    printf("0\n");
                else
                {
                    int res = query(root,x);
                    printf("%d\n",res);
                    update(root,res,res+x-1,1);
                }
            }
            else
            {
                scanf("%d %d",&x,&y);
                update(root,x,x+y-1,0);
            }
        }
    }
    return 0;
}


HDU 4417  Super Mario

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

题意:
就是给出一组数,求在每一组询问区间中小于给定数值的数量。
思路:
离线处理所有询问,对数组排序,依次加入,同时线段树求出加入该点前询问中区间的数量和就是对应答案。
code:
#define N 112345
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

int n,m;
struct node
{
    int x,y;
    int r,w;
    int to,next;
    friend bool operator < (node a, node b)
    {
        return a.r < b.r;
    }
}a[N],b[N];
bool cmp(node x,node y)
{
	return x.to < y.to;
}

int sum[N<<2],add2[N<<2];

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

void pushDown2(int l,int r,int rt)
{
    if(add2[rt])
    {
        int m = (l+r)>>1;
        add2[rt<<1] += add2[rt];
        add2[rt<<1|1] += add2[rt];
        sum[rt<<1] += (m-l+1)*add2[rt];
        sum[rt<<1|1] += (r-m)*add2[rt];
        add2[rt] = 0;
    }
}

void update2(int l,int r,int rt,int ql,int qr,int val)
{
    if(l>qr||ql>r)return;
    if(l>=ql&&r<=qr)
    {
        sum[rt] += (r-l+1)*val;
        add2[rt] += val;
        return;
    }
    pushDown2(l,r,rt);
    int m = (l+r)>>1;
    if(ql<=m)update2(lson,ql,qr,val);
    if(qr>m)update2(rson,ql,qr,val);
    pushUp(rt);
}
void build(int l,int r,int rt)
{
    add2[rt]=0;
    if(l == r)
    {
        sum[rt]=0;
        return;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    pushUp(rt);
}
int query(int l,int r,int rt,int ql,int qr)
{
    if(l>qr||ql>r)
        return 0;
    if(l>=ql&&r<=qr)
        return sum[rt];
    pushDown2(l,r,rt);
    int m = l+r>>1;
    return query(l,m,rt<<1,ql,qr)+query(m+1,r,rt<<1|1,ql,qr);
}
int main()
{
    int i,j,k,kk,t,x,y,z;
    scanf("%d",&k);
    kk=0;
    while(k--)
    {
        printf("Case %d:\n",++kk);
        scanf("%d%d",&n,&m);
        for(i=0;i<n;i++)
        {
            scanf("%d",&a[i].r);
            a[i].x=i+1;
        }
        for(i=0;i<m;i++)
        {
            scanf("%d %d %d",&b[i].x,&b[i].y,&b[i].r);
            b[i].to=i;
        }
        sort(a,a+n);
        sort(b,b+m);
        build(root);
        for(i=0,j=0;i<m;i++)
        {
            x=b[i].x+1;y=b[i].y+1;z=b[i].r;
            while(a[j].r<=z&&j<n)
            {
                update2(root,a[j].x,a[j].x,1);
                j++;
            }
            b[i].w=query(root,x,y);
        }
        sort(b,b+m,cmp);
        for(i=0;i<m;i++)
            printf("%d\n",b[i].w);
    }
    return 0;
}

HDU 4521 小明序列

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

题意:
中文题,良心。

给出一组数求其中的最长上升子序列,要求每两个数之间距离至少大于d。

思路:
数组升序排列,依次加入,该点的值就是1到Xi-d之间的最大值+1,就相当于是在该数前面的所有比它小的数中的最长上升子序列加上该数。

注意相同的数要降序加入更新,防止相同的数重复加和。

code:
#define N 112345
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

int n,m;
int flag,ave,ans,res;
struct node
{
    int x,y;
    int r,w;
    int to;
    friend bool operator < (node a, node b)
    {
        if(a.r==b.r)return a.x > b.x;
        return a.r < b.r;
    }
}a[N];
bool cmp(node x,node y)
{
	return x.to < y.to;
}

int sum[N<<2];

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

void update2(int l,int r,int rt,int ql,int qr,int val)
{
    if(l>qr||ql>r)return;
    if(l>=ql&&r<=qr)
    {
        sum[rt] = (r-l+1)*val;
        return;
    }
    int m = (l+r)>>1;
    if(ql<=m)update2(lson,ql,qr,val);
    if(qr>m)update2(rson,ql,qr,val);
    pushUp(rt);
}

void build(int l,int r,int rt)
{
    if(l == r)
    {
        sum[rt]=0;
        return;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    pushUp(rt);
}
int query(int l,int r,int rt,int ql,int qr)
{
    if(l>qr||ql>r)
        return 0;
    if(l>=ql&&r<=qr)
        return sum[rt];
    int m = l+r>>1;
    return max(query(l,m,rt<<1,ql,qr),query(m+1,r,rt<<1|1,ql,qr));
}
int main()
{
    int i,j,k,kk,t,x,y,z;
    while(scanf("%d%d",&n,&m)!=EOF&&n)
    {
        for(i=0;i<n;i++)
        {
            scanf("%d",&a[i].r);
            a[i].x=i+1;
        }
        sort(a,a+n);
        build(root);
        res=1;
        for(i=0;i<n;i++)
        {
            x=a[i].x;
            ans=0;
            if(x-m>0)
            {
                ans=query(root,1,x-m-1)+1;
                update2(root,x,x,ans);
                res=max(res,ans);
            }
            else
                update2(root,x,x,1);
        }
        printf("%d\n",res);
    }
    return 0;
}

HDU 3308 LCIS

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

题意:
求数列中的最长连续上升子序列;

有数字更新和区间询问两种操作。

思路:
线段树区间合并,与Hotel那题相似,开三个数组存储线段中最大序列和左右边界最大序列。再开两个数组储存每个节点左右端点的大小。

合并时如果左儿子的右端点小于右儿子的左端点则将他们的中间共有区间加入判断。

询问时注意如果要把共有区间加入判断时,要注意判断共有区间的端点位置,取询问区间和共有连续上升子序列的并集。

code:
#define N 112345
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

int n,m;
int sum[N<<2],lsum[N<<2],rsum[N<<2],ls[N<<2],rs[N<<2],add1[N<<2];
void pushUp(int l,int r,int rt)
{
    sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
    if(rs[rt<<1]<ls[rt<<1|1])
        sum[rt]=max(sum[rt],rsum[rt<<1]+lsum[rt<<1|1]);
    int mid=r-l+1;
    if(lsum[rt<<1]==mid-mid/2&&rs[rt<<1]<ls[rt<<1|1])
        lsum[rt]=lsum[rt<<1]+lsum[rt<<1|1];
    else
        lsum[rt]=lsum[rt<<1];
    if(mid/2==rsum[rt<<1|1]&&rs[rt<<1]<ls[rt<<1|1])
        rsum[rt]=rsum[rt<<1]+rsum[rt<<1|1];
    else
        rsum[rt]=rsum[rt<<1|1];
    rs[rt]=rs[rt<<1|1];
    ls[rt]=ls[rt<<1];
}

void update1(int l,int r,int rt,int ql,int qr,int val)
{
    if(l>qr||ql>r)return;
    if(l>=ql&&r<=qr)
    {
        sum[rt] = lsum[rt] = rsum[rt] = 1;
        ls[rt] = rs[rt] = val;
        return;
    }
    int m = (l+r)>>1;
    if(ql<=m)update1(lson,ql,qr,val);
    if(qr>m)update1(rson,ql,qr,val);
    pushUp(l,r,rt);
}

void build(int l,int r,int rt)
{
    add1[rt]=-1;
    if(l == r)
    {
        sum[rt]=lsum[rt]=rsum[rt]=1;
        scanf("%d",&rs[rt]);
        ls[rt] = rs[rt];
        return;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    pushUp(l,r,rt);
}

int query(int l,int r,int rt,int ql,int qr)
{
    if(l>qr||ql>r)
        return 0;
    if(l>=ql&&r<=qr)
        return sum[rt];
    int m = l+r>>1;
    if(rs[rt<<1]<ls[rt<<1|1])
        return max(min(qr,m+lsum[rt<<1|1])-max(ql,m+1-rsum[rt<<1])+1,max(query(l,m,rt<<1,ql,qr),query(m+1,r,rt<<1|1,ql,qr)));
    return max(query(l,m,rt<<1,ql,qr),query(m+1,r,rt<<1|1,ql,qr));
}

int main()
{
    int i,j,k,kk,t,x,y,z;
    scanf("%d",&k);
    while(k--)
    {
        scanf("%d %d",&n,&m);
        build(root);
        for(i=0;i<m;i++)
        {
            scanf("%s",s);
            if(s[0]=='Q')
            {
                scanf("%d%d",&x,&y);
                x++;y++;
                printf("%d\n",query(root,x,y));
            }
            else if(s[0]=='U')
            {
                scanf("%d%d",&x,&y);
                x++;
                update1(root,x,x,y);
            }
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值