HDU_3308_线段树_区间合并

LCIS

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6166    Accepted Submission(s): 2675

Problem Description

Given n integers.
You have two operations:
U A B: replace the Ath number by B. (index counting from 0)
Q A B: output the length of the longest consecutive increasing subsequence (LCIS) in [a, b].

Input

T in the first line, indicating the case number.
Each case starts with two integers n , m(0<n,m<=105).
The next line has n integers(0<=val<=105).
The next m lines each has an operation:
U A B(0<=A,n , 0<=B=105)
OR
Q A B(0<=A<=B< n).

Output

For each Q, output the answer.

Sample Input

1
10 10
7 7 3 3 5 9 9 8 1 8
Q 6 6
U 3 4
Q 0 1
Q 0 5
Q 4 7
Q 3 5
Q 0 2
Q 4 6
U 6 10
Q 0 9

Sample Output

1
1
4
2
3
1
2
5

第一道区间合并的线段树。

看了题解,从下午做到晚上。。。然而看网上说是一道简单的区间合并。。。桑心。。。

给出序列,可单点更新,求最长连续递增子列的长度。

每一段记录

struct Node
{
  int l,r,len;  //线段树中的左右端点,以及序列长度
  int ln,rn;   //这个线段上的序列左右端点
  int lm,rm,nm;   //包含左端点,包含右端点,整段的最长递增连续子列长度
} tree[maxn<<2];

一个序列(rt)中的最长连续递增子列的长度(nm)是 max(tree[rt<<1].nm,tree[rt<<1|1].nm)和满足tree[rt<<1].rn<tree[rt<<1|1].ln(左孩子的右端点小于右孩子的左端点)条件下的tree[rt<<1].rm+tree[rt<<1|1].lm中较大者。更新点后pushup和query就是用这个思想。

第一波超时,是因为mid=(l+r)/2,用位运算更快,mid=(l+r)>>1

之前查询函数写的有问题。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 100005
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

struct Node
{
    int l,r,len;
    int ln,rn;
    int lm,rm,nm;
} tree[maxn<<2];

int num[100005];

void pushup(int rt)
{
    tree[rt].ln=tree[rt<<1].ln;
    tree[rt].rn=tree[rt<<1|1].rn;
    tree[rt].lm=tree[rt<<1].lm;
    tree[rt].rm=tree[rt<<1|1].rm;
    tree[rt].nm=max(tree[rt<<1].nm,tree[rt<<1|1].nm);
    if(tree[rt<<1].rn<tree[rt<<1|1].ln)
    {
        if(tree[rt].lm==tree[rt<<1].len)
            tree[rt].lm+=tree[rt<<1|1].lm;
        if(tree[rt].rm==tree[rt<<1|1].len)
            tree[rt].rm+=tree[rt<<1].rm;
        tree[rt].nm=max(tree[rt].nm,tree[rt<<1].rm+tree[rt<<1|1].lm);
    }
}

void build(int l,int r,int rt)
{
    tree[rt].l=l;
    tree[rt].r=r;
    tree[rt].len=r-l+1;
    if(l==r)
    {
        tree[rt].lm=tree[rt].rm=tree[rt].nm=1;
        tree[rt].ln=tree[rt].rn=num[l];
        return;
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt);
}

void update(int pos,int x,int l,int r,int rt)
{
    if(l==pos&&r==pos)
    {
        tree[rt].ln=tree[rt].rn=x;
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid)
        update(pos,x,lson);
    else
        update(pos,x,rson);
    pushup(rt);
}

int query(int L,int R,int l,int r,int rt)
{
    if(L==l&&r==R)
        return tree[rt].nm;
    int mid=(l+r)>>1;
    if(R<=mid)
        return query(L,R,lson);
    else if(L>mid)
        return query(L,R,rson);
    else
    {
        int ll=query(L,mid,lson),ans=0;
        int rr=query(mid+1,R,rson);
        if(tree[rt<<1].rn<tree[rt<<1|1].ln)
            ans=min(mid-L+1,tree[rt<<1].rm)+min(R-mid,tree[rt<<1|1].lm);
        return max(ans,max(ll,rr));
    }
}
/*
int query(int l,int r,int i)//查询最大的LCIS
{
    if(tree[i].l>=l && tree[i].r<=r)
    {
        return tree[i].nm;
    }
    int mid = (tree[i].l+tree[i].r)>>1,ans = 0;
    if(l<=mid)
        ans = max(ans,query(l,r,2*i));
    if(r>mid)
        ans = max(ans,query(l,r,2*i+1));
    if(tree[2*i].rn < tree[2*i+1].ln)
        ans = max(ans , min(mid-l+1,tree[2*i].rm)+min(r-mid,tree[2*i+1].lm));
    return ans;
}*/

int main()
{
    int t,n,m;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; i++)
            scanf("%d",&num[i]);
        build(1,n,1);
        //cout<<tree[9].nm<<endl;
        //cout<<"*"<<endl;

        for(int i=0; i<m; i++)
        {
            char str[2];
            int a,b;
            scanf("%s%d%d",str,&a,&b);
            if(str[0]==‘U‘)
                update(a+1,b,1,n,1);
            else if(str[0]==‘Q‘)
                printf("%d\n",query(a+1,b+1,1,n,1));
        }
    }
    return 0;
}
时间: 2024-10-27 01:34:12

HDU_3308_线段树_区间合并的相关文章

HDU 1540 &amp;&amp; POJ 2892 Tunnel Warfare (线段树,区间合并).

~~~~ 第一次遇到线段树合并的题,又被律爷教做人.TAT. ~~~~ 线段树的题意都很好理解吧.. 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1540 http://poj.org/problem?id=2892 ~~~~ 我的代码:200ms #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #defin

POJ 3667 线段树的区间合并简单问题

题目大意:有一排标号1-N的房间.操作一:询问是不是有连续长度为a的空房间,有的话住进最左边(占用a个房间)操作二:将[a,a+b-1]的房间清空(腾出b个房间)思路:记录每个区间中“靠左”“靠右”“中间”的空房间线段树操作:update:区间替换query:询问满足条件的最左端点 题目链接: http://vjudge.net/problem/viewProblem.action?id=10354 题目操作: 因为这里找从最左边住起的房间,所以这里不能像常规地写query函数那样写了,终于发现

HDU 3308 线段树(区间合并)

LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3896    Accepted Submission(s): 1766 Problem Description Given n integers.You have two operations:U A B: replace the Ath number by B. (index

Codeforces Round #222 (Div. 1) D. Developing Game 线段树有效区间合并

D. Developing Game Pavel is going to make a game of his dream. However, he knows that he can't make it on his own so he founded a development company and hired n workers of staff. Now he wants to pick n workers from the staff who will be directly res

线段树_区间加乘(洛谷P3373模板)

题目描述 如题,已知一个数列,你需要进行下面三种操作: 1.将某区间每一个数乘上x 2.将某区间每一个数加上x 3.求出某区间每一个数的和 输入格式: 第一行包含三个整数N.M.P,分别表示该数列数字的个数.操作的总个数和模数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数乘上k 操作2: 格式:2 x y k 含义:将区间[x,y]内每个数加上k

[HDOJ3308]LCIS(线段树,区间合并)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 题意:给定n个数,两个操作: U A B:将位置A的数值改成B Q A B:查询[A,B]内最长连续上升子序列的长度. 注意到‘连续’一词,可以用线段树维护[L,R]区间内的LICS. 定义结构Node,内部ls,rs为左右儿子的下标.l,r记录当前区间分别从左起和右起的LICS长度,s记录整个区间内的LICS长度. pushup:和一般的区间合并操作一样,但是要注意假如合并的左右子树中间有可

POJ--2892--Tunnel Warfare【线段树】区间合并

链接:http://poj.org/problem?id=2892 题意:有n个村庄排成一排,三种操作: 1. D x 摧毁村庄x 2. Q x 询问村庄x的最长一段没有被摧毁的村庄数量 3. R   恢复上一个被摧毁的村庄 思路:线段树区间合并,lsum记录当前节点往左的最长连续距离,rsum记录当前节点往右的最长连续距离. #include<cstring> #include<string> #include<fstream> #include<iostrea

HDU 3308 线段树之区间合并

点击打开链接 题意:T组数据,每组n和m,代表n个数和m次操作,U代表将第a个数的值改为b,Q代表询问a~b区间的最长连续上升子序列的长度,严格上升的 思路:一看到询问多少次了这种,肯定是线段树不用想,问区间的最长上升,可以用区间合并,lnum代表从区间左第一个元素开始的最长上升长度,注意第一个元素必须有,rnum代表从区间必须有最后一个元素的最长上升子序列,,mmnum代表区间最长的上升子序列,而num现在存的并不是树了,而是数据,具体的还有注释 #include <stdio.h> #in

【POJ】3667-Hotel(线段树的区间合并)

标记线段树的时候利用 lsum rsum msum 记录最左边 zui右边 以及整个区间的最大连续空位的值 维护的时候注意合并 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define lson (pos<<1) #define rson (pos<<1|1) const int maxn = 55555; int n,m; struct